From: Linus Torvalds Date: Wed, 27 Jul 2016 01:59:59 +0000 (-0700) Subject: Merge tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab... X-Git-Tag: v4.8-rc1~152 X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=9c1958fc326a0a0a533ec8e86ea6fa30977207de;hp=1b3fc0bef8859268d542230172f80e85553fdab4;p=linux.git Merge tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: - new framework support for HDMI CEC and remote control support - new encoding codec driver for Mediatek SoC - new frontend driver: helene tuner - added support for NetUp almost universal devices, with supports DVB-C/S/S2/T/T2 and ISDB-T - the mn88472 frontend driver got promoted from staging - a new driver for RCar video input - some soc_camera legacy drivers got removed: timb, omap1, mx2, mx3 - lots of driver cleanups, improvements and fixups * tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (377 commits) [media] cec: always check all_device_types and features [media] cec: poll should check if there is room in the tx queue [media] vivid: support monitor all mode [media] cec: fix test for unconfigured adapter in main message loop [media] cec: limit the size of the transmit queue [media] cec: zero unused msg part after msg->len [media] cec: don't set fh to NULL in CEC_TRANSMIT [media] cec: clear all status fields before transmit and always fill in sequence [media] cec: CEC_RECEIVE overwrote the timeout field [media] cxd2841er: Reading SNR for DVB-C added [media] cxd2841er: Reading BER and UCB for DVB-C added [media] cxd2841er: fix switch-case for DVB-C [media] cxd2841er: fix signal strength scale for ISDB-T [media] cxd2841er: adjust the dB scale for DVB-C [media] cxd2841er: provide signal strength for DVB-C [media] cxd2841er: fix BER report via DVBv5 stats API [media] mb86a20s: apply mask to val after checking for read failure [media] airspy: fix error logic during device register [media] s5p-cec/TODO: add TODO item [media] cec/TODO: drop comment about sphinx documentation ... --- diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 8c68768ebee5..58af32b01b90 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -300,6 +300,9 @@ X!Isound/sound_firmware.c !Iinclude/media/media-devnode.h !Iinclude/media/media-entity.h + Consumer Electronics Control devices +!Iinclude/media/cec-edid.h + diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 2840ff483d5a..fdc138624800 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -64,6 +64,7 @@ IOCTLS = \ $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \ $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \ $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \ + $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/cec.h) \ $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ DEFINES = \ @@ -100,6 +101,7 @@ STRUCTS = \ $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \ $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \ $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \ + $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/cec.h) \ $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \ $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h) diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml index 9beb30f0071b..87f1d24958aa 100644 --- a/Documentation/DocBook/media/v4l/biblio.xml +++ b/Documentation/DocBook/media/v4l/biblio.xml @@ -342,6 +342,16 @@ in the frequency range from 87,5 to 108,0 MHz Specification Version 1.4a + + HDMI2 + + HDMI Licensing LLC +(http://www.hdmi.org) + + High-Definition Multimedia Interface + Specification Version 2.0 + + DP diff --git a/Documentation/DocBook/media/v4l/cec-api.xml b/Documentation/DocBook/media/v4l/cec-api.xml new file mode 100644 index 000000000000..7062c1fa4904 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-api.xml @@ -0,0 +1,75 @@ + + + + Hans + Verkuil +
hans.verkuil@cisco.com
+ Initial version. +
+
+ + 2016 + Hans Verkuil + + + + + + 1.0.0 + 2016-03-17 + hv + Initial revision + + +
+ +CEC API + + + CEC: Consumer Electronics Control + +
+ Introduction + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + HDMI connectors provide a single pin for use by the Consumer Electronics + Control protocol. This protocol allows different devices connected by an HDMI cable + to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC) + and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a + () specification and the extensions added to CEC version 2.0 + are defined in chapter 11 of the HDMI 2.0 () specification. + + + The bitrate is very slow (effectively no more than 36 bytes per second) and + is based on the ancient AV.link protocol used in old SCART connectors. The protocol + closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and + high level messages. Some messages, especially those part of the HEAC protocol layered + on top of CEC, need to be handled by the kernel, others can be handled either by the + kernel or by userspace. + + In addition, CEC can be implemented in HDMI receivers, transmitters and in USB + devices that have an HDMI input and an HDMI output and that control just the CEC pin. + + Drivers that support CEC will create a CEC device node (/dev/cecX) + to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace + what it is allowed to do. +
+
+ + + Function Reference + + &sub-cec-func-open; + &sub-cec-func-close; + &sub-cec-func-ioctl; + &sub-cec-func-poll; + + &sub-cec-ioc-adap-g-caps; + &sub-cec-ioc-adap-g-log-addrs; + &sub-cec-ioc-adap-g-phys-addr; + &sub-cec-ioc-dqevent; + &sub-cec-ioc-g-mode; + &sub-cec-ioc-receive; + diff --git a/Documentation/DocBook/media/v4l/cec-func-close.xml b/Documentation/DocBook/media/v4l/cec-func-close.xml new file mode 100644 index 000000000000..0812c8cd9634 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-func-close.xml @@ -0,0 +1,64 @@ + + + cec close() + &manvol; + + + + cec-close + Close a cec device + + + + + #include <unistd.h> + + int close + int fd + + + + + + Arguments + + + + fd + + &fd; + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + Closes the cec device. Resources associated with the file descriptor + are freed. The device configuration remain unchanged. + + + + Return Value + + close returns 0 on success. On error, -1 is + returned, and errno is set appropriately. Possible error + codes are: + + + + EBADF + + fd is not a valid open file descriptor. + + + + + + diff --git a/Documentation/DocBook/media/v4l/cec-func-ioctl.xml b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml new file mode 100644 index 000000000000..f92817a2dc80 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml @@ -0,0 +1,78 @@ + + + cec ioctl() + &manvol; + + + + cec-ioctl + Control a cec device + + + + + #include <sys/ioctl.h> + + int ioctl + int fd + int request + void *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + CEC ioctl request code as defined in the cec.h header file, + for example CEC_ADAP_G_CAPS. + + + + argp + + Pointer to a request-specific structure. + + + + + + + Description + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + The ioctl() function manipulates cec device + parameters. The argument fd must be an open file + descriptor. + The ioctl request code specifies the cec + function to be called. It has encoded in it whether the argument is an + input, output or read/write parameter, and the size of the argument + argp in bytes. + Macros and structures definitions specifying cec ioctl requests and + their parameters are located in the cec.h header file. All cec ioctl + requests, their respective function and parameters are specified in + . + + + + &return-value; + + Request-specific error codes are listed in the + individual requests descriptions. + When an ioctl that takes an output or read/write parameter fails, + the parameter remains unmodified. + + diff --git a/Documentation/DocBook/media/v4l/cec-func-open.xml b/Documentation/DocBook/media/v4l/cec-func-open.xml new file mode 100644 index 000000000000..2edc5555b81a --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-func-open.xml @@ -0,0 +1,104 @@ + + + cec open() + &manvol; + + + + cec-open + Open a cec device + + + + + #include <fcntl.h> + + int open + const char *device_name + int flags + + + + + + Arguments + + + + device_name + + Device to be opened. + + + + flags + + Open flags. Access mode must be O_RDWR. + + When the O_NONBLOCK flag is +given, the &CEC-RECEIVE; ioctl will return &EAGAIN; when no message is +available, and the &CEC-TRANSMIT;, &CEC-ADAP-S-PHYS-ADDR; and +&CEC-ADAP-S-LOG-ADDRS; ioctls all act in non-blocking mode. + Other flags have no effect. + + + + + + Description + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + To open a cec device applications call open() + with the desired device name. The function has no side effects; the device + configuration remain unchanged. + When the device is opened in read-only mode, attempts to modify its + configuration will result in an error, and errno will be + set to EBADF. + + + Return Value + + open returns the new file descriptor on success. + On error, -1 is returned, and errno is set appropriately. + Possible error codes include: + + + + EACCES + + The requested access to the file is not allowed. + + + + EMFILE + + The process already has the maximum number of files open. + + + + + ENFILE + + The system limit on the total number of open files has been + reached. + + + + ENOMEM + + Insufficient kernel memory was available. + + + + ENXIO + + No device corresponding to this device special file exists. + + + + + + diff --git a/Documentation/DocBook/media/v4l/cec-func-poll.xml b/Documentation/DocBook/media/v4l/cec-func-poll.xml new file mode 100644 index 000000000000..1bddbde0142d --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-func-poll.xml @@ -0,0 +1,94 @@ + + + cec poll() + &manvol; + + + + cec-poll + Wait for some event on a file descriptor + + + + + #include <sys/poll.h> + + int poll + struct pollfd *ufds + unsigned int nfds + int timeout + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + With the poll() function applications +can wait for CEC events. + + On success poll() returns the number of +file descriptors that have been selected (that is, file descriptors +for which the revents field of the +respective pollfd structure is non-zero). +CEC devices set the POLLIN and +POLLRDNORM flags in the +revents field if there are messages in the +receive queue. If the transmit queue has room for new messages, the +POLLOUT and POLLWRNORM +flags are set. If there are events in the event queue, then the +POLLPRI flag is set. +When the function timed out it returns a value of zero, on +failure it returns -1 and the +errno variable is set appropriately. + + + For more details see the +poll() manual page. + + + + Return Value + + On success, poll() returns the number +structures which have non-zero revents +fields, or zero if the call timed out. On error +-1 is returned, and the +errno variable is set appropriately: + + + + EBADF + + One or more of the ufds members +specify an invalid file descriptor. + + + + EFAULT + + ufds references an inaccessible +memory area. + + + + EINTR + + The call was interrupted by a signal. + + + + EINVAL + + The nfds argument is greater +than OPEN_MAX. + + + + + diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml new file mode 100644 index 000000000000..3523ef2259b1 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml @@ -0,0 +1,151 @@ + + + ioctl CEC_ADAP_G_CAPS + &manvol; + + + + CEC_ADAP_G_CAPS + Query device capabilities + + + + + + int ioctl + int fd + int request + struct cec_caps *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_ADAP_G_CAPS + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + All cec devices must support the CEC_ADAP_G_CAPS + ioctl. To query device information, applications call the ioctl with a + pointer to a &cec-caps;. The driver fills the structure and returns + the information to the application. + The ioctl never fails. + + + struct <structname>cec_caps</structname> + + &cs-str; + + + char + driver[32] + The name of the cec adapter driver. + + + char + name[32] + The name of this CEC adapter. The combination driver + and name must be unique. + + + __u32 + capabilities + The capabilities of the CEC adapter, see . + + + __u32 + version + CEC Framework API version, formatted with the + KERNEL_VERSION() macro. + + + +
+ + + CEC Capabilities Flags + + &cs-def; + + + CEC_CAP_PHYS_ADDR + 0x00000001 + Userspace has to configure the physical address by + calling &CEC-ADAP-S-PHYS-ADDR;. If this capability isn't set, + then setting the physical address is handled by the kernel + whenever the EDID is set (for an HDMI receiver) or read (for + an HDMI transmitter). + + + CEC_CAP_LOG_ADDRS + 0x00000002 + Userspace has to configure the logical addresses by + calling &CEC-ADAP-S-LOG-ADDRS;. If this capability isn't set, + then the kernel will have configured this. + + + CEC_CAP_TRANSMIT + 0x00000004 + Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This + implies that userspace can be a follower as well, since being able to + transmit messages is a prerequisite of becoming a follower. If this + capability isn't set, then the kernel will handle all CEC transmits + and process all CEC messages it receives. + + + + CEC_CAP_PASSTHROUGH + 0x00000008 + Userspace can use the passthrough mode by + calling &CEC-S-MODE;. + + + CEC_CAP_RC + 0x00000010 + This adapter supports the remote control protocol. + + + CEC_CAP_MONITOR_ALL + 0x00000020 + The CEC hardware can monitor all messages, not just directed and + broadcast messages. + + + +
+
+ + + &return-value; + +
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml new file mode 100644 index 000000000000..302b8294f7fc --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml @@ -0,0 +1,329 @@ + + + ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS + &manvol; + + + + CEC_ADAP_G_LOG_ADDRS + CEC_ADAP_S_LOG_ADDRS + Get or set the logical addresses + + + + + + int ioctl + int fd + int request + struct cec_log_addrs *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + To query the current CEC logical addresses, applications call the +CEC_ADAP_G_LOG_ADDRS ioctl with a pointer to a +cec_log_addrs structure where the drivers stores the +logical addresses. + + To set new logical addresses, applications fill in struct cec_log_addrs +and call the CEC_ADAP_S_LOG_ADDRS ioctl with a pointer to this struct. +The CEC_ADAP_S_LOG_ADDRS ioctl is only available if +CEC_CAP_LOG_ADDRS is set (&ENOTTY; is returned otherwise). This ioctl will block until all +requested logical addresses have been claimed. CEC_ADAP_S_LOG_ADDRS +can only be called by a file handle in initiator mode (see &CEC-S-MODE;). + + + struct <structname>cec_log_addrs</structname> + + &cs-str; + + + __u8 + log_addr[CEC_MAX_LOG_ADDRS] + The actual logical addresses that were claimed. This is set by the + driver. If no logical address could be claimed, then it is set to + CEC_LOG_ADDR_INVALID. If this adapter is Unregistered, + then log_addr[0] is set to 0xf and all others to + CEC_LOG_ADDR_INVALID. + + + __u16 + log_addr_mask + The bitmask of all logical addresses this adapter has claimed. + If this adapter is Unregistered then log_addr_mask + sets bit 15 and clears all other bits. If this adapter is not configured at all, then + log_addr_mask is set to 0. Set by the driver. + + + __u8 + cec_version + The CEC version that this adapter shall use. See + . + Used to implement the CEC_MSG_CEC_VERSION and + CEC_MSG_REPORT_FEATURES messages. Note that + CEC_OP_CEC_VERSION_1_3A is not allowed + by the CEC framework. + + + + __u8 + num_log_addrs + Number of logical addresses to set up. Must be ≤ + available_log_addrs as returned by + &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to + index available_log_addrs-1. The remaining + array elements will be ignored. Note that the CEC 2.0 standard allows + for a maximum of 2 logical addresses, although some hardware has support + for more. CEC_MAX_LOG_ADDRS is 4. The driver will + return the actual number of logical addresses it could claim, which may + be less than what was requested. If this field is set to 0, then the + CEC adapter shall clear all claimed logical addresses and all other + fields will be ignored. + + + __u32 + vendor_id + The vendor ID is a 24-bit number that identifies the specific + vendor or entity. Based on this ID vendor specific commands may be + defined. If you do not want a vendor ID then set it to + CEC_VENDOR_ID_NONE. + + + __u32 + flags + Flags. No flags are defined yet, so set this to 0. + + + char + osd_name[15] + The On-Screen Display name as is returned by the + CEC_MSG_SET_OSD_NAME message. + + + __u8 + primary_device_type[CEC_MAX_LOG_ADDRS] + Primary device type for each logical address. See + for possible types. + + + __u8 + log_addr_type[CEC_MAX_LOG_ADDRS] + Logical address types. See for + possible types. The driver will update this with the actual logical address + type that it claimed (e.g. it may have to fallback to + CEC_LOG_ADDR_TYPE_UNREGISTERED). + + + __u8 + all_device_types[CEC_MAX_LOG_ADDRS] + CEC 2.0 specific: all device types. See . + Used to implement the CEC_MSG_REPORT_FEATURES message. + This field is ignored if cec_version < + CEC_OP_CEC_VERSION_2_0. + + + __u8 + features[CEC_MAX_LOG_ADDRS][12] + Features for each logical address. Used to implement the + CEC_MSG_REPORT_FEATURES message. The 12 bytes include + both the RC Profile and the Device Features. + This field is ignored if cec_version < + CEC_OP_CEC_VERSION_2_0. + + + +
+ + + CEC Versions + + &cs-def; + + + CEC_OP_CEC_VERSION_1_3A + 4 + CEC version according to the HDMI 1.3a standard. + + + CEC_OP_CEC_VERSION_1_4B + 5 + CEC version according to the HDMI 1.4b standard. + + + CEC_OP_CEC_VERSION_2_0 + 6 + CEC version according to the HDMI 2.0 standard. + + + +
+ + + CEC Primary Device Types + + &cs-def; + + + CEC_OP_PRIM_DEVTYPE_TV + 0 + Use for a TV. + + + CEC_OP_PRIM_DEVTYPE_RECORD + 1 + Use for a recording device. + + + CEC_OP_PRIM_DEVTYPE_TUNER + 3 + Use for a device with a tuner. + + + CEC_OP_PRIM_DEVTYPE_PLAYBACK + 4 + Use for a playback device. + + + CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM + 5 + Use for an audio system (e.g. an audio/video receiver). + + + CEC_OP_PRIM_DEVTYPE_SWITCH + 6 + Use for a CEC switch. + + + CEC_OP_PRIM_DEVTYPE_VIDEOPROC + 7 + Use for a video processor device. + + + +
+ + + CEC Logical Address Types + + &cs-def; + + + CEC_LOG_ADDR_TYPE_TV + 0 + Use for a TV. + + + CEC_LOG_ADDR_TYPE_RECORD + 1 + Use for a recording device. + + + CEC_LOG_ADDR_TYPE_TUNER + 2 + Use for a tuner device. + + + CEC_LOG_ADDR_TYPE_PLAYBACK + 3 + Use for a playback device. + + + CEC_LOG_ADDR_TYPE_AUDIOSYSTEM + 4 + Use for an audio system device. + + + CEC_LOG_ADDR_TYPE_SPECIFIC + 5 + Use for a second TV or for a video processor device. + + + CEC_LOG_ADDR_TYPE_UNREGISTERED + 6 + Use this if you just want to remain unregistered. + Used for pure CEC switches or CDC-only devices (CDC: + Capability Discovery and Control). + + + +
+ + + CEC All Device Types Flags + + &cs-def; + + + CEC_OP_ALL_DEVTYPE_TV + 0x80 + This supports the TV type. + + + CEC_OP_ALL_DEVTYPE_RECORD + 0x40 + This supports the Recording type. + + + CEC_OP_ALL_DEVTYPE_TUNER + 0x20 + This supports the Tuner type. + + + CEC_OP_ALL_DEVTYPE_PLAYBACK + 0x10 + This supports the Playback type. + + + CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM + 0x08 + This supports the Audio System type. + + + CEC_OP_ALL_DEVTYPE_SWITCH + 0x04 + This supports the CEC Switch or Video Processing type. + + + +
+
+ + + &return-value; + +
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml new file mode 100644 index 000000000000..d95f1785080c --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml @@ -0,0 +1,86 @@ + + + ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR + &manvol; + + + + CEC_ADAP_G_PHYS_ADDR + CEC_ADAP_S_PHYS_ADDR + Get or set the physical address + + + + + + int ioctl + int fd + int request + __u16 *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + To query the current physical address applications call the +CEC_ADAP_G_PHYS_ADDR ioctl with a pointer to an __u16 +where the driver stores the physical address. + + To set a new physical address applications store the physical address in +an __u16 and call the CEC_ADAP_S_PHYS_ADDR ioctl with a +pointer to this integer. CEC_ADAP_S_PHYS_ADDR is only +available if CEC_CAP_PHYS_ADDR is set (&ENOTTY; will be returned +otherwise). CEC_ADAP_S_PHYS_ADDR +can only be called by a file handle in initiator mode (see &CEC-S-MODE;), if not +&EBUSY; will be returned. + + The physical address is a 16-bit number where each group of 4 bits +represent a digit of the physical address a.b.c.d where the most significant +4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0. +Every device that is hooked up to an input of the TV has address a.0.0.0 (where +'a' is ≥ 1), devices hooked up to those in turn have addresses a.b.0.0, etc. +So a topology of up to 5 devices deep is supported. The physical address a +device shall use is stored in the EDID of the sink. + +For example, the EDID for each HDMI input of the TV will have a different +physical address of the form a.0.0.0 that the sources will read out and use as +their physical address. + + + + &return-value; + + diff --git a/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml new file mode 100644 index 000000000000..697dde575cd4 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml @@ -0,0 +1,202 @@ + + + ioctl CEC_DQEVENT + &manvol; + + + + CEC_DQEVENT + Dequeue a CEC event + + + + + + int ioctl + int fd + int request + struct cec_event *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_DQEVENT + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + CEC devices can send asynchronous events. These can be retrieved by calling + the CEC_DQEVENT ioctl. If the file descriptor is in non-blocking + mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;. + + The internal event queues are per-filehandle and per-event type. If there is + no more room in a queue then the last event is overwritten with the new one. This + means that intermediate results can be thrown away but that the latest event is always + available. This also means that is it possible to read two successive events that have + the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that + case the intermediate state changes were lost but it is guaranteed that the state + did change in between the two events. + + + struct <structname>cec_event_state_change</structname> + + &cs-str; + + + __u16 + phys_addr + The current physical address. + + + __u16 + log_addr_mask + The current set of claimed logical addresses. + + + +
+ + + struct <structname>cec_event_lost_msgs</structname> + + &cs-str; + + + __u32 + lost_msgs + Set to the number of lost messages since the filehandle + was opened or since the last time this event was dequeued for + this filehandle. The messages lost are the oldest messages. So + when a new message arrives and there is no more room, then the + oldest message is discarded to make room for the new one. The + internal size of the message queue guarantees that all messages + received in the last two seconds will be stored. Since messages + should be replied to within a second according to the CEC + specification, this is more than enough. + + + + +
+ + + struct <structname>cec_event</structname> + + &cs-str; + + + __u64 + ts + Timestamp of the event in ns. + + + + __u32 + event + The CEC event type, see . + + + + __u32 + flags + Event flags, see . + + + + union + (anonymous) + + + + + + struct cec_event_state_change + state_change + The new adapter state as sent by the CEC_EVENT_STATE_CHANGE + event. + + + + struct cec_event_lost_msgs + lost_msgs + The number of lost messages as sent by the CEC_EVENT_LOST_MSGS + event. + + + +
+ + + CEC Events Types + + &cs-def; + + + CEC_EVENT_STATE_CHANGE + 1 + Generated when the CEC Adapter's state changes. When open() is + called an initial event will be generated for that filehandle with the + CEC Adapter's state at that time. + + + + CEC_EVENT_LOST_MSGS + 2 + Generated if one or more CEC messages were lost because the + application didn't dequeue CEC messages fast enough. + + + +
+ + + CEC Event Flags + + &cs-def; + + + CEC_EVENT_FL_INITIAL_VALUE + 1 + Set for the initial events that are generated when the device is + opened. See the table above for which events do this. This allows + applications to learn the initial state of the CEC adapter at open() + time. + + + +
+
+ + + &return-value; + +
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml new file mode 100644 index 000000000000..26b4282ad134 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml @@ -0,0 +1,255 @@ + + + ioctl CEC_G_MODE, CEC_S_MODE + &manvol; + + + + CEC_G_MODE + CEC_S_MODE + Get or set exclusive use of the CEC adapter + + + + + + int ioctl + int fd + int request + __u32 *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_G_MODE, CEC_S_MODE + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but +in order to prevent applications from stepping on each others toes it must be possible +to obtain exclusive access to the CEC adapter. This ioctl sets the filehandle +to initiator and/or follower mode which can be exclusive depending on the chosen +mode. The initiator is the filehandle that is used +to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle +that receives messages sent to the CEC adapter and processes them. The same filehandle +can be both initiator and follower, or this role can be taken by two different +filehandles. + + When a CEC message is received, then the CEC framework will decide how +it will be processed. If the message is a reply to an earlier transmitted message, +then the reply is sent back to the filehandle that is waiting for it. In addition +the CEC framework will process it. + + If the message is not a reply, then the CEC framework will process it +first. If there is no follower, then the message is just discarded and a feature +abort is sent back to the initiator if the framework couldn't process it. If there +is a follower, then the message is passed on to the follower who will use +&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to +make the right decisions. + + The CEC framework will process core messages unless requested otherwise +by the follower. The follower can enable the passthrough mode. In that case, the +CEC framework will pass on most core messages without processing them and +the follower will have to implement those messages. There are some messages +that the core will always process, regardless of the passthrough mode. See + for details. + + If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;. +If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;. +The follower can of course always call &CEC-TRANSMIT;. + + Available initiator modes are: + + + Initiator Modes + + &cs-def; + + + CEC_MODE_NO_INITIATOR + 0x0 + This is not an initiator, i.e. it cannot transmit CEC messages + or make any other changes to the CEC adapter. + + + CEC_MODE_INITIATOR + 0x1 + This is an initiator (the default when the device is opened) and it + can transmit CEC messages and make changes to the CEC adapter, unless there + is an exclusive initiator. + + + CEC_MODE_EXCL_INITIATOR + 0x2 + This is an exclusive initiator and this file descriptor is the only one + that can transmit CEC messages and make changes to the CEC adapter. If someone + else is already the exclusive initiator then an attempt to become one will return + the &EBUSY; error. + + + +
+ + Available follower modes are: + + + Follower Modes + + &cs-def; + + + CEC_MODE_NO_FOLLOWER + 0x00 + This is not a follower (the default when the device is opened). + + + CEC_MODE_FOLLOWER + 0x10 + This is a follower and it will receive CEC messages unless there is + an exclusive follower. You cannot become a follower if CEC_CAP_TRANSMIT + is not set or if CEC_MODE_NO_INITIATOR was specified, + &EINVAL; is returned in that case. + + + CEC_MODE_EXCL_FOLLOWER + 0x20 + This is an exclusive follower and only this file descriptor will receive + CEC messages for processing. If someone else is already the exclusive follower + then an attempt to become one will return the &EBUSY; error. You cannot become + a follower if CEC_CAP_TRANSMIT is not set or if + CEC_MODE_NO_INITIATOR was specified, &EINVAL; is returned + in that case. + + + CEC_MODE_EXCL_FOLLOWER_PASSTHRU + 0x30 + This is an exclusive follower and only this file descriptor will receive + CEC messages for processing. In addition it will put the CEC device into + passthrough mode, allowing the exclusive follower to handle most core messages + instead of relying on the CEC framework for that. If someone else is already the + exclusive follower then an attempt to become one will return the &EBUSY; error. + You cannot become a follower if CEC_CAP_TRANSMIT + is not set or if CEC_MODE_NO_INITIATOR was specified, + &EINVAL; is returned in that case. + + + CEC_MODE_MONITOR + 0xe0 + Put the file descriptor into monitor mode. Can only be used in combination + with CEC_MODE_NO_INITIATOR, otherwise &EINVAL; will be + returned. In monitor mode all messages this CEC device transmits and all messages + it receives (both broadcast messages and directed messages for one its logical + addresses) will be reported. This is very useful for debugging. This is only + allowed if the process has the CAP_NET_ADMIN + capability. If that is not set, then &EPERM; is returned. + + + CEC_MODE_MONITOR_ALL + 0xf0 + Put the file descriptor into 'monitor all' mode. Can only be used in combination + with CEC_MODE_NO_INITIATOR, otherwise &EINVAL; will be + returned. In 'monitor all' mode all messages this CEC device transmits and all messages + it receives, including directed messages for other CEC devices will be reported. This + is very useful for debugging, but not all devices support this. This mode requires that + the CEC_CAP_MONITOR_ALL capability is set, otherwise &EINVAL; is + returned. This is only allowed if the process has the CAP_NET_ADMIN + capability. If that is not set, then &EPERM; is returned. + + + +
+ + Core message processing details: + + + Core Message Processing + + &cs-def; + + + CEC_MSG_GET_CEC_VERSION + When in passthrough mode this message has to be handled by userspace, + otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;. + + + CEC_MSG_GIVE_DEVICE_VENDOR_ID + When in passthrough mode this message has to be handled by userspace, + otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;. + + + CEC_MSG_ABORT + When in passthrough mode this message has to be handled by userspace, + otherwise the core will return a feature refused message as per the specification. + + + CEC_MSG_GIVE_PHYSICAL_ADDR + When in passthrough mode this message has to be handled by userspace, + otherwise the core will report the current physical address. + + + CEC_MSG_GIVE_OSD_NAME + When in passthrough mode this message has to be handled by userspace, + otherwise the core will report the current OSD name as was set with + &CEC-ADAP-S-LOG-ADDRS;. + + + CEC_MSG_GIVE_FEATURES + When in passthrough mode this message has to be handled by userspace, + otherwise the core will report the current features as was set with + &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was + older than 2.0. + + + CEC_MSG_USER_CONTROL_PRESSED + If CEC_CAP_RC is set, then generate a remote control + key press. This message is always passed on to userspace. + + + CEC_MSG_USER_CONTROL_RELEASED + If CEC_CAP_RC is set, then generate a remote control + key release. This message is always passed on to userspace. + + + CEC_MSG_REPORT_PHYSICAL_ADDR + The CEC framework will make note of the reported physical address + and then just pass the message on to userspace. + + + +
+
+ + + &return-value; + +
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-receive.xml b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml new file mode 100644 index 000000000000..fde9f8678e67 --- /dev/null +++ b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml @@ -0,0 +1,274 @@ + + + ioctl CEC_RECEIVE, CEC_TRANSMIT + &manvol; + + + + CEC_RECEIVE + CEC_TRANSMIT + Receive or transmit a CEC message + + + + + + int ioctl + int fd + int request + struct cec_msg *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + CEC_RECEIVE, CEC_TRANSMIT + + + + argp + + + + + + + + + Description + + + Note: this documents the proposed CEC API. This API is not yet finalized and + is currently only available as a staging kernel module. + + + To receive a CEC message the application has to fill in the + cec_msg structure and pass it to the + CEC_RECEIVE ioctl. CEC_RECEIVE is + only available if CEC_CAP_RECEIVE is set. If the + file descriptor is in non-blocking mode and there are no received + messages pending, then it will return -1 and set errno to the &EAGAIN;. + If the file descriptor is in blocking mode and timeout + is non-zero and no message arrived within timeout + milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;. + + To send a CEC message the application has to fill in the + cec_msg structure and pass it to the + CEC_TRANSMIT ioctl. CEC_TRANSMIT is + only available if CEC_CAP_TRANSMIT is set. + If there is no more room in the transmit queue, then it will return + -1 and set errno to the &EBUSY;. + + + struct <structname>cec_msg</structname> + + &cs-str; + + + __u64 + ts + Timestamp of when the message was transmitted in ns in the case + of CEC_TRANSMIT with reply + set to 0, or the timestamp of the received message in all other cases. + + + __u32 + len + The length of the message. For CEC_TRANSMIT this + is filled in by the application. The driver will fill this in for + CEC_RECEIVE and for CEC_TRANSMIT + it will be filled in with the length of the reply message if + reply was set. + + + __u32 + timeout + The timeout in milliseconds. This is the time the device will wait for a message to + be received before timing out. If it is set to 0, then it will wait indefinitely when it + is called by CEC_RECEIVE. If it is 0 and it is called by + CEC_TRANSMIT, then it will be replaced by 1000 if the + reply is non-zero or ignored if reply + is 0. + + + __u32 + sequence + The sequence number is automatically assigned by the CEC + framework for all transmitted messages. It can be later used by the + framework to generate an event if a reply for a message was + requested and the message was transmitted in a non-blocking mode. + + + + __u32 + flags + Flags. No flags are defined yet, so set this to 0. + + + __u8 + rx_status + The status bits of the received message. See + for the possible status values. It is 0 if this message was transmitted, not + received, unless this is the reply to a transmitted message. In that case both + rx_status and tx_status + are set. + + + __u8 + tx_status + The status bits of the transmitted message. See + for the possible status values. It is 0 if this messages was received, not + transmitted. + + + __u8 + msg[16] + The message payload. For CEC_TRANSMIT this + is filled in by the application. The driver will fill this in for + CEC_RECEIVE and for CEC_TRANSMIT + it will be filled in with the payload of the reply message if + reply was set. + + + __u8 + reply + Wait until this message is replied. If reply + is 0 and the timeout is 0, then don't wait for a reply but + return after transmitting the message. If there was an error as indicated by a non-zero + tx_status field, then reply and + timeout are both set to 0 by the driver. Ignored by + CEC_RECEIVE. The case where reply is 0 + (this is the opcode for the Feature Abort message) and timeout + is non-zero is specifically allowed to send a message and wait up to timeout + milliseconds for a Feature Abort reply. In this case rx_status + will either be set to CEC_RX_STATUS_TIMEOUT or + CEC_RX_STATUS_FEATURE_ABORT. + + + __u8 + tx_arb_lost_cnt + A counter of the number of transmit attempts that resulted in the + Arbitration Lost error. This is only set if the hardware supports this, otherwise + it is always 0. This counter is only valid if the CEC_TX_STATUS_ARB_LOST + status bit is set. + + + __u8 + tx_nack_cnt + A counter of the number of transmit attempts that resulted in the + Not Acknowledged error. This is only set if the hardware supports this, otherwise + it is always 0. This counter is only valid if the CEC_TX_STATUS_NACK + status bit is set. + + + __u8 + tx_low_drive_cnt + A counter of the number of transmit attempts that resulted in the + Arbitration Lost error. This is only set if the hardware supports this, otherwise + it is always 0. This counter is only valid if the CEC_TX_STATUS_LOW_DRIVE + status bit is set. + + + __u8 + tx_error_cnt + A counter of the number of transmit errors other than Arbitration Lost + or Not Acknowledged. This is only set if the hardware supports this, otherwise + it is always 0. This counter is only valid if the CEC_TX_STATUS_ERROR + status bit is set. + + + +
+ + + CEC Transmit Status + + &cs-def; + + + CEC_TX_STATUS_OK + 0x01 + The message was transmitted successfully. This is mutually exclusive with + CEC_TX_STATUS_MAX_RETRIES. Other bits can still be set if + earlier attempts met with failure before the transmit was eventually successful. + + + CEC_TX_STATUS_ARB_LOST + 0x02 + CEC line arbitration was lost. + + + CEC_TX_STATUS_NACK + 0x04 + Message was not acknowledged. + + + CEC_TX_STATUS_LOW_DRIVE + 0x08 + Low drive was detected on the CEC bus. This indicates that a follower + detected an error on the bus and requests a retransmission. + + + CEC_TX_STATUS_ERROR + 0x10 + Some error occurred. This is used for any errors that do not + fit the previous two, either because the hardware could not tell + which error occurred, or because the hardware tested for other conditions + besides those two. + + + CEC_TX_STATUS_MAX_RETRIES + 0x20 + The transmit failed after one or more retries. This status bit is mutually + exclusive with CEC_TX_STATUS_OK. Other bits can still be set + to explain which failures were seen. + + + +
+ + + CEC Receive Status + + &cs-def; + + + CEC_RX_STATUS_OK + 0x01 + The message was received successfully. + + + CEC_RX_STATUS_TIMEOUT + 0x02 + The reply to an earlier transmitted message timed out. + + + CEC_RX_STATUS_FEATURE_ABORT + 0x04 + The message was received successfully but the reply was + CEC_MSG_FEATURE_ABORT. This status is only + set if this message was the reply to an earlier transmitted + message. + + + +
+
+ + + &return-value; + +
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index e09025db92bd..21a3dde8f95d 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -88,7 +88,7 @@ function. capabilities field of &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl is set. There are two streaming methods, to determine if the memory mapping flavor is -supported applications must call the &VIDIOC-REQBUFS; ioctl. +supported applications must call the &VIDIOC-REQBUFS; ioctl with the memory type set to V4L2_MEMORY_MMAP. Streaming is an I/O method where only pointers to buffers are exchanged between application and driver, the data itself is not @@ -369,7 +369,7 @@ rest should be evident. capabilities field of &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl is set. If the particular user pointer method (not only memory mapping) is supported must be -determined by calling the &VIDIOC-REQBUFS; ioctl. +determined by calling the &VIDIOC-REQBUFS; ioctl with the memory type set to V4L2_MEMORY_USERPTR. This I/O method combines advantages of the read/write and memory mapping methods. Buffers (planes) are allocated by the application diff --git a/Documentation/DocBook/media/v4l/lirc_device_interface.xml b/Documentation/DocBook/media/v4l/lirc_device_interface.xml index 34cada2ca710..71f9dbb81ec7 100644 --- a/Documentation/DocBook/media/v4l/lirc_device_interface.xml +++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml @@ -157,7 +157,7 @@ on working with the default settings initially. LIRC_SET_{SEND,REC}_CARRIER - Set send/receive carrier (in Hz). + Set send/receive carrier (in Hz). Return 0 on success. diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml index 5e3f20fdcf17..95aa1f9c836a 100644 --- a/Documentation/DocBook/media/v4l/media-types.xml +++ b/Documentation/DocBook/media/v4l/media-types.xml @@ -121,6 +121,70 @@ MEDIA_ENT_F_AUDIO_MIXER Audio Mixer Function Entity. + + MEDIA_ENT_F_PROC_VIDEO_COMPOSER + Video composer (blender). An entity capable of video + composing must have at least two sink pads and one source + pad, and composes input video frames onto output video + frames. Composition can be performed using alpha blending, + color keying, raster operations (ROP), stitching or any other + means. + + + + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER + Video pixel formatter. An entity capable of pixel formatting + must have at least one sink pad and one source pad. Read + pixel formatters read pixels from memory and perform a subset + of unpacking, cropping, color keying, alpha multiplication + and pixel encoding conversion. Write pixel formatters perform + a subset of dithering, pixel encoding conversion and packing + and write pixels to memory. + + + + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV + Video pixel encoding converter. An entity capable of pixel + enconding conversion must have at least one sink pad and one + source pad, and convert the encoding of pixels received on + its sink pad(s) to a different encoding output on its source + pad(s). Pixel encoding conversion includes but isn't limited + to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB + conversions. + + + + MEDIA_ENT_F_PROC_VIDEO_LUT + Video look-up table. An entity capable of video lookup table + processing must have one sink pad and one source pad. It uses + the values of the pixels received on its sink pad to look up + entries in internal tables and output them on its source pad. + The lookup processing can be performed on all components + separately or combine them for multi-dimensional table + lookups. + + + + MEDIA_ENT_F_PROC_VIDEO_SCALER + Video scaler. An entity capable of video scaling must have + at least one sink pad and one source pad, and scale the + video frame(s) received on its sink pad(s) to a different + resolution output on its source pad(s). The range of + supported scaling ratios is entity-specific and can differ + between the horizontal and vertical directions (in particular + scaling can be supported in one direction only). Binning and + skipping are considered as scaling. + + + + MEDIA_ENT_F_PROC_VIDEO_STATISTICS + Video statistics computation (histogram, 3A, ...). An entity + capable of statistics computation must have one sink pad and + one source pad. It computes statistics over the frames + received on its sink pad and outputs the statistics data on + its source pad. + + diff --git a/Documentation/DocBook/media/v4l/pixfmt-z16.xml b/Documentation/DocBook/media/v4l/pixfmt-z16.xml index 3d87e4bf87b8..1d9cb1684bd3 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-z16.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-z16.xml @@ -5,7 +5,7 @@ V4L2_PIX_FMT_Z16 - Interleaved grey-scale image, e.g. from a stereo-pair + 16-bit depth data with distance values at each pixel Description diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index 0f193fda0470..6f529e100ea4 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -6,7 +6,7 @@ VIDIOC_REQBUFS - Initiate Memory Mapping or User Pointer I/O + Initiate Memory Mapping, User Pointer or DMA Buffer I/O diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index 7b77e0f7b87d..a2765d8ad05c 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -75,7 +75,7 @@ The media infrastructure API was designed to control such - devices. It is divided into four parts. + devices. It is divided into five parts. The first part covers radio, video capture and output, cameras, analog TV devices and codecs. The second part covers the @@ -87,6 +87,7 @@ . The third part covers the Remote Controller API. The fourth part covers the Media Controller API. + The fifth part covers the CEC (Consumer Electronics Control) API. It should also be noted that a media device may also have audio components, like mixers, PCM capture, PCM playback, etc, which are controlled via ALSA API. @@ -107,6 +108,9 @@ &sub-media-controller; + +&sub-cec-api; + &sub-gen-errors; diff --git a/Documentation/cec.txt b/Documentation/cec.txt new file mode 100644 index 000000000000..75155fe37153 --- /dev/null +++ b/Documentation/cec.txt @@ -0,0 +1,267 @@ +CEC Kernel Support +================== + +The CEC framework provides a unified kernel interface for use with HDMI CEC +hardware. It is designed to handle a multiple types of hardware (receivers, +transmitters, USB dongles). The framework also gives the option to decide +what to do in the kernel driver and what should be handled by userspace +applications. In addition it integrates the remote control passthrough +feature into the kernel's remote control framework. + + +The CEC Protocol +---------------- + +The CEC protocol enables consumer electronic devices to communicate with each +other through the HDMI connection. The protocol uses logical addresses in the +communication. The logical address is strictly connected with the functionality +provided by the device. The TV acting as the communication hub is always +assigned address 0. The physical address is determined by the physical +connection between devices. + +The CEC framework described here is up to date with the CEC 2.0 specification. +It is documented in the HDMI 1.4 specification with the new 2.0 bits documented +in the HDMI 2.0 specification. But for most of the features the freely available +HDMI 1.3a specification is sufficient: + +http://www.microprocessor.org/HDMISpecification13a.pdf + + +The Kernel Interface +==================== + +CEC Adapter +----------- + +The struct cec_adapter represents the CEC adapter hardware. It is created by +calling cec_allocate_adapter() and deleted by calling cec_delete_adapter(): + +struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, + void *priv, const char *name, u32 caps, u8 available_las, + struct device *parent); +void cec_delete_adapter(struct cec_adapter *adap); + +To create an adapter you need to pass the following information: + +ops: adapter operations which are called by the CEC framework and that you +have to implement. + +priv: will be stored in adap->priv and can be used by the adapter ops. + +name: the name of the CEC adapter. Note: this name will be copied. + +caps: capabilities of the CEC adapter. These capabilities determine the + capabilities of the hardware and which parts are to be handled + by userspace and which parts are handled by kernelspace. The + capabilities are returned by CEC_ADAP_G_CAPS. + +available_las: the number of simultaneous logical addresses that this + adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS. + +parent: the parent device. + + +To register the /dev/cecX device node and the remote control device (if +CEC_CAP_RC is set) you call: + +int cec_register_adapter(struct cec_adapter *adap); + +To unregister the devices call: + +void cec_unregister_adapter(struct cec_adapter *adap); + +Note: if cec_register_adapter() fails, then call cec_delete_adapter() to +clean up. But if cec_register_adapter() succeeded, then only call +cec_unregister_adapter() to clean up, never cec_delete_adapter(). The +unregister function will delete the adapter automatically once the last user +of that /dev/cecX device has closed its file handle. + + +Implementing the Low-Level CEC Adapter +-------------------------------------- + +The following low-level adapter operations have to be implemented in +your driver: + +struct cec_adap_ops { + /* Low-level callbacks */ + int (*adap_enable)(struct cec_adapter *adap, bool enable); + int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + void (*adap_log_status)(struct cec_adapter *adap); + + /* High-level callbacks */ + ... +}; + +The three low-level ops deal with various aspects of controlling the CEC adapter +hardware: + + +To enable/disable the hardware: + + int (*adap_enable)(struct cec_adapter *adap, bool enable); + +This callback enables or disables the CEC hardware. Enabling the CEC hardware +means powering it up in a state where no logical addresses are claimed. This +op assumes that the physical address (adap->phys_addr) is valid when enable is +true and will not change while the CEC adapter remains enabled. The initial +state of the CEC adapter after calling cec_allocate_adapter() is disabled. + +Note that adap_enable must return 0 if enable is false. + + +To enable/disable the 'monitor all' mode: + + int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); + +If enabled, then the adapter should be put in a mode to also monitor messages +that not for us. Not all hardware supports this and this function is only +called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional +(some hardware may always be in 'monitor all' mode). + +Note that adap_monitor_all_enable must return 0 if enable is false. + + +To program a new logical address: + + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + +If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses +are to be erased. Otherwise the given logical address should be programmed. +If the maximum number of available logical addresses is exceeded, then it +should return -ENXIO. Once a logical address is programmed the CEC hardware +can receive directed messages to that address. + +Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID. + + +To transmit a new message: + + int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + +This transmits a new message. The attempts argument is the suggested number of +attempts for the transmit. + +The signal_free_time is the number of data bit periods that the adapter should +wait when the line is free before attempting to send a message. This value +depends on whether this transmit is a retry, a message from a new initiator or +a new message for the same initiator. Most hardware will handle this +automatically, but in some cases this information is needed. + +The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to +microseconds (one data bit period is 2.4 ms). + + +To log the current CEC hardware status: + + void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); + +This optional callback can be used to show the status of the CEC hardware. +The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status + + +Your adapter driver will also have to react to events (typically interrupt +driven) by calling into the framework in the following situations: + +When a transmit finished (successfully or otherwise): + +void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, + u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); + +The status can be one of: + +CEC_TX_STATUS_OK: the transmit was successful. +CEC_TX_STATUS_ARB_LOST: arbitration was lost: another CEC initiator +took control of the CEC line and you lost the arbitration. +CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or +acked (for a broadcast message). A retransmission is needed. +CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This +indicates that a follower detected an error on the bus and requested a +retransmission. +CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of +the previous two if the hardware cannot differentiate or something else +entirely. +CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after +trying multiple times. Should only be set by the driver if it has hardware +support for retrying messages. If set, then the framework assumes that it +doesn't have to make another attempt to transmit the message since the +hardware did that already. + +The *_cnt arguments are the number of error conditions that were seen. +This may be 0 if no information is available. Drivers that do not support +hardware retry can just set the counter corresponding to the transmit error +to 1, if the hardware does support retry then either set these counters to +0 if the hardware provides no feedback of which errors occurred and how many +times, or fill in the correct values as reported by the hardware. + +When a CEC message was received: + +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); + +Speaks for itself. + +Implementing the High-Level CEC Adapter +--------------------------------------- + +The low-level operations drive the hardware, the high-level operations are +CEC protocol driven. The following high-level callbacks are available: + +struct cec_adap_ops { + /* Low-level callbacks */ + ... + + /* High-level CEC message callback */ + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); +}; + +The received() callback allows the driver to optionally handle a newly +received CEC message + + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); + +If the driver wants to process a CEC message, then it can implement this +callback. If it doesn't want to handle this message, then it should return +-ENOMSG, otherwise the CEC framework assumes it processed this message and +it will not no anything with it. + + +CEC framework functions +----------------------- + +CEC Adapter drivers can call the following CEC framework functions: + +int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, + bool block); + +Transmit a CEC message. If block is true, then wait until the message has been +transmitted, otherwise just queue it and return. + +void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); + +Change the physical address. This function will set adap->phys_addr and +send an event if it has changed. If cec_s_log_addrs() has been called and +the physical address has become valid, then the CEC framework will start +claiming the logical addresses. If block is true, then this function won't +return until this process has finished. + +When the physical address is set to a valid value the CEC adapter will +be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID, +then the CEC adapter will be disabled. If you change a valid physical address +to another valid physical address, then this function will first set the +address to CEC_PHYS_ADDR_INVALID before enabling the new physical address. + +int cec_s_log_addrs(struct cec_adapter *adap, + struct cec_log_addrs *log_addrs, bool block); + +Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS +is set. If block is true, then wait until the logical addresses have been +claimed, otherwise just queue it and return. To unconfigure all logical +addresses call this function with log_addrs set to NULL or with +log_addrs->num_log_addrs set to 0. The block argument is ignored when +unconfiguring. This function will just return if the physical address is +invalid. Once the physical address becomes valid, then the framework will +attempt to claim these logical addresses. diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt new file mode 100644 index 000000000000..59a47a5b924b --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt @@ -0,0 +1,59 @@ +Mediatek Video Codec + +Mediatek Video Codec is the video codec hw present in Mediatek SoCs which +supports high resolution encoding functionalities. + +Required properties: +- compatible : "mediatek,mt8173-vcodec-enc" for encoder +- reg : Physical base address of the video codec registers and length of + memory mapped region. +- interrupts : interrupt number to the cpu. +- mediatek,larb : must contain the local arbiters in the current Socs. +- clocks : list of clock specifiers, corresponding to entries in + the clock-names property. +- clock-names: encoder must contain "venc_sel_src", "venc_sel", +- "venc_lt_sel_src", "venc_lt_sel". +- iommus : should point to the respective IOMMU block with master port as + argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt + for details. +- mediatek,vpu : the node of video processor unit + +Example: +vcodec_enc: vcodec@0x18002000 { + compatible = "mediatek,mt8173-vcodec-enc"; + reg = <0 0x18002000 0 0x1000>, /*VENC_SYS*/ + <0 0x19002000 0 0x1000>; /*VENC_LT_SYS*/ + interrupts = , + ; + mediatek,larb = <&larb3>, + <&larb5>; + iommus = <&iommu M4U_PORT_VENC_RCPU>, + <&iommu M4U_PORT_VENC_REC>, + <&iommu M4U_PORT_VENC_BSDMA>, + <&iommu M4U_PORT_VENC_SV_COMV>, + <&iommu M4U_PORT_VENC_RD_COMV>, + <&iommu M4U_PORT_VENC_CUR_LUMA>, + <&iommu M4U_PORT_VENC_CUR_CHROMA>, + <&iommu M4U_PORT_VENC_REF_LUMA>, + <&iommu M4U_PORT_VENC_REF_CHROMA>, + <&iommu M4U_PORT_VENC_NBM_RDMA>, + <&iommu M4U_PORT_VENC_NBM_WDMA>, + <&iommu M4U_PORT_VENC_RCPU_SET2>, + <&iommu M4U_PORT_VENC_REC_FRM_SET2>, + <&iommu M4U_PORT_VENC_BSDMA_SET2>, + <&iommu M4U_PORT_VENC_SV_COMA_SET2>, + <&iommu M4U_PORT_VENC_RD_COMA_SET2>, + <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>, + <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>, + <&iommu M4U_PORT_VENC_REF_LUMA_SET2>, + <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>; + mediatek,vpu = <&vpu>; + clocks = <&topckgen CLK_TOP_VENCPLL_D2>, + <&topckgen CLK_TOP_VENC_SEL>, + <&topckgen CLK_TOP_UNIVPLL1_D2>, + <&topckgen CLK_TOP_VENC_LT_SEL>; + clock-names = "venc_sel_src", + "venc_sel", + "venc_lt_sel_src", + "venc_lt_sel"; + }; diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt new file mode 100644 index 000000000000..2a5bac37f9a2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt @@ -0,0 +1,31 @@ +* Mediatek Video Processor Unit + +Video Processor Unit is a HW video controller. It controls HW Codec including +H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert). + +Required properties: + - compatible: "mediatek,mt8173-vpu" + - reg: Must contain an entry for each entry in reg-names. + - reg-names: Must include the following entries: + "tcm": tcm base + "cfg_reg": Main configuration registers base + - interrupts: interrupt number to the cpu. + - clocks : clock name from clock manager + - clock-names: must be main. It is the main clock of VPU + +Optional properties: + - memory-region: phandle to a node describing memory (see + Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt) + to be used for VPU extended memory; if not present, VPU may be located + anywhere in the memory + +Example: + vpu: vpu@10020000 { + compatible = "mediatek,mt8173-vpu"; + reg = <0 0x10020000 0 0x30000>, + <0 0x10050000 0 0x100>; + reg-names = "tcm", "cfg_reg"; + interrupts = ; + clocks = <&topckgen TOP_SCP_SEL>; + clock-names = "main"; + }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt new file mode 100644 index 000000000000..6a12960609d8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fcp.txt @@ -0,0 +1,32 @@ +Renesas R-Car Frame Compression Processor (FCP) +----------------------------------------------- + +The FCP is a companion module of video processing modules in the Renesas R-Car +Gen3 SoCs. It provides data compression and decompression, data caching, and +conversion of AXI transactions in order to reduce the memory bandwidth. + +There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP +for FDP (FCPF). Their configuration and behaviour depend on the module they +are paired with. These DT bindings currently support the FCPV only. + + - compatible: Must be one or more of the following + + - "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP' + - "renesas,fcpv" for generic compatible 'FCP for VSP' + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first, followed by the + family-specific and/or generic versions. + + - reg: the register base and size for the device registers + - clocks: Reference to the functional clock + + +Device node example +------------------- + + fcpvd1: fcp@fea2f000 { + compatible = "renesas,r8a7795-fcpv", "renesas,fcpv"; + reg = <0 0xfea2f000 0 0x200>; + clocks = <&cpg CPG_MOD 602>; + }; diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt index 627405abd144..9b695bcbf219 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.txt @@ -14,6 +14,11 @@ Required properties: - interrupts: VSP interrupt specifier. - clocks: A phandle + clock-specifier pair for the VSP functional clock. +Optional properties: + + - renesas,fcp: A phandle referencing the FCP that handles memory accesses + for the VSP. Not needed on Gen2, mandatory on Gen3. + Example: R8A7790 (R-Car H2) VSP1-S node diff --git a/Documentation/devicetree/bindings/media/s5p-cec.txt b/Documentation/devicetree/bindings/media/s5p-cec.txt new file mode 100644 index 000000000000..925ab4d72eaa --- /dev/null +++ b/Documentation/devicetree/bindings/media/s5p-cec.txt @@ -0,0 +1,31 @@ +* Samsung HDMI CEC driver + +The HDMI CEC module is present is Samsung SoCs and its purpose is to +handle communication between HDMI connected devices over the CEC bus. + +Required properties: + - compatible : value should be following + "samsung,s5p-cec" + + - reg : Physical base address of the IP registers and length of memory + mapped region. + + - interrupts : HDMI CEC interrupt number to the CPU. + - clocks : from common clock binding: handle to HDMI CEC clock. + - clock-names : from common clock binding: must contain "hdmicec", + corresponding to entry in the clocks property. + - samsung,syscon-phandle - phandle to the PMU system controller + +Example: + +hdmicec: cec@100B0000 { + compatible = "samsung,s5p-cec"; + reg = <0x100B0000 0x200>; + interrupts = <0 114 0>; + clocks = <&clock CLK_HDMI_CEC>; + clock-names = "hdmicec"; + samsung,syscon-phandle = <&pmu_system_controller>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_cec>; + status = "okay"; +}; diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index 2d5787eac91a..92c94f5ecbf1 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -21,15 +21,18 @@ Required properties: - clock-names : from common clock binding: must contain "mfc", corresponding to entry in the clocks property. - - samsung,mfc-r : Base address of the first memory bank used by MFC - for DMA contiguous memory allocation and its size. - - - samsung,mfc-l : Base address of the second memory bank used by MFC - for DMA contiguous memory allocation and its size. - Optional properties: - power-domains : power-domain property defined with a phandle to respective power domain. + - memory-region : from reserved memory binding: phandles to two reserved + memory regions, first is for "left" mfc memory bus interfaces, + second if for the "right" mfc memory bus, used when no SYSMMU + support is available + +Obsolete properties: + - samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region + property instead + Example: SoC specific DT entry: @@ -43,9 +46,29 @@ mfc: codec@13400000 { clock-names = "mfc"; }; +Reserved memory specific DT entry for given board (see reserved memory binding +for more information): + +reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; +}; + Board specific DT entry: codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 85a8fdcfcdaa..c9b4959fd04e 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -54,3 +54,4 @@ 53 -> Hauppauge WinTV Starburst [0070:c12a] 54 -> ViewCast 260e [1576:0260] 55 -> ViewCast 460e [1576:0460] + 56 -> Hauppauge WinTV-quadHD (DVB) [0070:6a28,0070:6b28] diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 5e759cab4538..f930b80e9111 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -96,21 +96,6 @@ Basic usage for V4L2 and sub-device drivers Where foo->sd is of type struct v4l2_subdev. - And set all core control ops in your struct v4l2_subdev_core_ops to these - helpers: - - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - - Note: this is a temporary solution only. Once all V4L2 drivers that depend - on subdev drivers are converted to the control framework these helpers will - no longer be needed. - 1.4) Clean up the handler at the end: v4l2_ctrl_handler_free(&foo->ctrl_handler); diff --git a/Documentation/video4linux/vivid.txt b/Documentation/video4linux/vivid.txt index 8da5d2a576bc..1b26519c6ddc 100644 --- a/Documentation/video4linux/vivid.txt +++ b/Documentation/video4linux/vivid.txt @@ -74,7 +74,8 @@ Section 11: Cropping, Composing, Scaling Section 12: Formats Section 13: Capture Overlay Section 14: Output Overlay -Section 15: Some Future Improvements +Section 15: CEC (Consumer Electronics Control) +Section 16: Some Future Improvements Section 1: Configuring the driver @@ -364,7 +365,11 @@ For HDMI inputs it is possible to set the EDID. By default a simple EDID is provided. You can only set the EDID for HDMI inputs. Internally, however, the EDID is shared between all HDMI inputs. -No interpretation is done of the EDID data. +No interpretation is done of the EDID data with the exception of the +physical address. See the CEC section for more details. + +There is a maximum of 15 HDMI inputs (if there are more, then they will be +reduced to 15) since that's the limitation of the EDID physical address. Section 3: Video Output @@ -409,6 +414,9 @@ standard, and for all others a 1:1 pixel aspect ratio is returned. An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID. +There is a maximum of 15 HDMI outputs (if there are more, then they will be +reduced to 15) since that's the limitation of the EDID physical address. See +also the CEC section for more details. Section 4: VBI Capture ---------------------- @@ -1108,7 +1116,26 @@ capabilities will slow down the video loop considerably as a lot of checks have to be done per pixel. -Section 15: Some Future Improvements +Section 15: CEC (Consumer Electronics Control) +---------------------------------------------- + +If there are HDMI inputs then a CEC adapter will be created that has +the same number of input ports. This is the equivalent of e.g. a TV that +has that number of inputs. Each HDMI output will also create a +CEC adapter that is hooked up to the corresponding input port, or (if there +are more outputs than inputs) is not hooked up at all. In other words, +this is the equivalent of hooking up each output device to an input port of +the TV. Any remaining output devices remain unconnected. + +The EDID that each output reads reports a unique CEC physical address that is +based on the physical address of the EDID of the input. So if the EDID of the +receiver has physical address A.B.0.0, then each output will see an EDID +containing physical address A.B.C.0 where C is 1 to the number of inputs. If +there are more outputs than inputs then the remaining outputs have a CEC adapter +that is disabled and reports an invalid physical address. + + +Section 16: Some Future Improvements ------------------------------------ Just as a reminder and in no particular order: @@ -1121,8 +1148,6 @@ Just as a reminder and in no particular order: - Fix sequence/field numbering when looping of video with alternate fields - Add support for V4L2_CID_BG_COLOR for video outputs - Add ARGB888 overlay support: better testing of the alpha channel -- Add custom DV timings support -- Add support for V4L2_DV_FL_REDUCED_FPS - Improve pixel aspect support in the tpg code by passing a real v4l2_fract - Use per-queue locks and/or per-device locks to improve throughput - Add support to loop from a specific output to a specific input across @@ -1133,3 +1158,4 @@ Just as a reminder and in no particular order: - Make a thread for the RDS generation, that would help in particular for the "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated in real-time. +- Changing the EDID should cause hotplug detect emulation to happen. diff --git a/MAINTAINERS b/MAINTAINERS index 1cf1beb4b366..0a3827b722b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1648,6 +1648,13 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/platform/s5p-tv/ +ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT +M: Kyungmin Park +L: linux-arm-kernel@lists.infradead.org +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/staging/media/platform/s5p-cec/ + ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT M: Andrzej Pietrasiewicz M: Jacek Anaszewski @@ -2851,6 +2858,22 @@ F: drivers/net/ieee802154/cc2520.c F: include/linux/spi/cc2520.h F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt +CEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Supported +F: Documentation/cec.txt +F: Documentation/DocBook/media/v4l/cec* +F: drivers/staging/media/cec/ +F: drivers/media/cec-edid.c +F: drivers/media/rc/keymaps/rc-cec.c +F: include/media/cec.h +F: include/media/cec-edid.h +F: include/linux/cec.h +F: include/linux/cec-funcs.h + CELL BROADBAND ENGINE ARCHITECTURE M: Arnd Bergmann L: linuxppc-dev@lists.ozlabs.org @@ -5177,10 +5200,10 @@ S: Maintained F: drivers/media/usb/gspca/m5602/ GSPCA PAC207 SONIXB SUBDRIVER -M: Hans de Goede +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -S: Maintained +S: Odd Fixes F: drivers/media/usb/gspca/pac207.c GSPCA SN9C20X SUBDRIVER @@ -5198,10 +5221,10 @@ S: Maintained F: drivers/media/usb/gspca/t613.c GSPCA USB WEBCAM DRIVER -M: Hans de Goede +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -S: Maintained +S: Odd Fixes F: drivers/media/usb/gspca/ GUID PARTITION TABLE (GPT) @@ -7344,6 +7367,16 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/potentiometer/mcp4531.c +MEDIA DRIVERS FOR RENESAS - FCP +M: Laurent Pinchart +L: linux-media@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/renesas,fcp.txt +F: drivers/media/platform/rcar-fcp.c +F: include/media/rcar-fcp.h + MEDIA DRIVERS FOR RENESAS - VSP1 M: Laurent Pinchart L: linux-media@vger.kernel.org @@ -7353,8 +7386,18 @@ S: Supported F: Documentation/devicetree/bindings/media/renesas,vsp1.txt F: drivers/media/platform/vsp1/ +MEDIA DRIVERS FOR HELENE +M: Abylay Ospan +L: linux-media@vger.kernel.org +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/helene* + MEDIA DRIVERS FOR ASCOT2E M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7364,6 +7407,7 @@ F: drivers/media/dvb-frontends/ascot2e* MEDIA DRIVERS FOR CXD2841ER M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7373,6 +7417,7 @@ F: drivers/media/dvb-frontends/cxd2841er* MEDIA DRIVERS FOR HORUS3A M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7382,6 +7427,7 @@ F: drivers/media/dvb-frontends/horus3a* MEDIA DRIVERS FOR LNBH25 M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7391,6 +7437,7 @@ F: drivers/media/dvb-frontends/lnbh25* MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7640,10 +7687,8 @@ L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ -T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/mn88472/ -F: drivers/media/dvb-frontends/mn88472.h +F: drivers/media/dvb-frontends/mn88472* MN88473 MEDIA DRIVER M: Antti Palosaari @@ -9255,6 +9300,13 @@ F: include/linux/tracehook.h F: include/uapi/linux/ptrace.h F: kernel/ptrace.c +PULSE8-CEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/staging/media/pulse8-cec + PVRUSB2 VIDEO4LINUX DRIVER M: Mike Isely L: pvrusb2@isely.net (subscribers-only) @@ -9266,10 +9318,10 @@ F: Documentation/video4linux/README.pvrusb2 F: drivers/media/usb/pvrusb2/ PWC WEBCAM DRIVER -M: Hans de Goede +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -S: Maintained +S: Odd Fixes F: drivers/media/usb/pwc/* PWM FAN DRIVER @@ -9485,14 +9537,14 @@ F: drivers/video/fbdev/aty/radeon* F: include/uapi/linux/radeonfb.h RADIOSHARK RADIO DRIVER -M: Hans de Goede +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/radio/radio-shark.c RADIOSHARK2 RADIO DRIVER -M: Hans de Goede +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Maintained diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index 05f89c4a5413..77b8c4e388ca 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -168,6 +168,18 @@ map@1 { }; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + vpu_dma_reserved: vpu_dma_mem_region { + compatible = "shared-dma-pool"; + reg = <0 0xb7000000 0 0x500000>; + alignment = <0x1000>; + no-map; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupt-parent = <&gic>; @@ -312,6 +324,17 @@ pwrap: pwrap@1000d000 { clock-names = "spi", "wrap"; }; + vpu: vpu@10020000 { + compatible = "mediatek,mt8173-vpu"; + reg = <0 0x10020000 0 0x30000>, + <0 0x10050000 0 0x100>; + reg-names = "tcm", "cfg_reg"; + interrupts = ; + clocks = <&topckgen CLK_TOP_SCP_SEL>; + clock-names = "main"; + memory-region = <&vpu_dma_reserved>; + }; + sysirq: intpol-controller@10200620 { compatible = "mediatek,mt8173-sysirq", "mediatek,mt6577-sysirq"; @@ -754,6 +777,45 @@ larb3: larb@18001000 { clock-names = "apb", "smi"; }; + vcodec_enc: vcodec@18002000 { + compatible = "mediatek,mt8173-vcodec-enc"; + reg = <0 0x18002000 0 0x1000>, /* VENC_SYS */ + <0 0x19002000 0 0x1000>; /* VENC_LT_SYS */ + interrupts = , + ; + mediatek,larb = <&larb3>, + <&larb5>; + iommus = <&iommu M4U_PORT_VENC_RCPU>, + <&iommu M4U_PORT_VENC_REC>, + <&iommu M4U_PORT_VENC_BSDMA>, + <&iommu M4U_PORT_VENC_SV_COMV>, + <&iommu M4U_PORT_VENC_RD_COMV>, + <&iommu M4U_PORT_VENC_CUR_LUMA>, + <&iommu M4U_PORT_VENC_CUR_CHROMA>, + <&iommu M4U_PORT_VENC_REF_LUMA>, + <&iommu M4U_PORT_VENC_REF_CHROMA>, + <&iommu M4U_PORT_VENC_NBM_RDMA>, + <&iommu M4U_PORT_VENC_NBM_WDMA>, + <&iommu M4U_PORT_VENC_RCPU_SET2>, + <&iommu M4U_PORT_VENC_REC_FRM_SET2>, + <&iommu M4U_PORT_VENC_BSDMA_SET2>, + <&iommu M4U_PORT_VENC_SV_COMA_SET2>, + <&iommu M4U_PORT_VENC_RD_COMA_SET2>, + <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>, + <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>, + <&iommu M4U_PORT_VENC_REF_LUMA_SET2>, + <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>; + mediatek,vpu = <&vpu>; + clocks = <&topckgen CLK_TOP_VENCPLL_D2>, + <&topckgen CLK_TOP_VENC_SEL>, + <&topckgen CLK_TOP_UNIVPLL1_D2>, + <&topckgen CLK_TOP_VENC_LT_SEL>; + clock-names = "venc_sel_src", + "venc_sel", + "venc_lt_sel_src", + "venc_lt_sel"; + }; + vencltsys: clock-controller@19000000 { compatible = "mediatek,mt8173-vencltsys", "syscon"; reg = <0 0x19000000 0 0x1000>; diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index aad5d7416886..9231e5a72b93 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -1002,14 +1002,12 @@ static struct adv7842_output_format adv7842_opf[] = { { .op_ch_sel = ADV7842_OP_CH_SEL_BRG, .op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_8, - .op_656_range = 1, .blank_data = 1, .insert_av_codes = 1, }, { .op_ch_sel = ADV7842_OP_CH_SEL_RGB, .op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_16, - .op_656_range = 1, .blank_data = 1, }, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index e671a7cd3463..6ac717f2056f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -148,40 +148,39 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) struct rcar_du_vsp_plane_state *state = to_rcar_vsp_plane_state(plane->plane.state); struct drm_framebuffer *fb = plane->plane.state->fb; - struct v4l2_rect src; - struct v4l2_rect dst; - dma_addr_t paddr[2] = { 0, }; - u32 pixelformat = 0; + struct vsp1_du_atomic_config cfg = { + .pixelformat = 0, + .pitch = fb->pitches[0], + .alpha = state->alpha, + .zpos = state->zpos, + }; unsigned int i; - src.left = state->state.src_x >> 16; - src.top = state->state.src_y >> 16; - src.width = state->state.src_w >> 16; - src.height = state->state.src_h >> 16; + cfg.src.left = state->state.src_x >> 16; + cfg.src.top = state->state.src_y >> 16; + cfg.src.width = state->state.src_w >> 16; + cfg.src.height = state->state.src_h >> 16; - dst.left = state->state.crtc_x; - dst.top = state->state.crtc_y; - dst.width = state->state.crtc_w; - dst.height = state->state.crtc_h; + cfg.dst.left = state->state.crtc_x; + cfg.dst.top = state->state.crtc_y; + cfg.dst.width = state->state.crtc_w; + cfg.dst.height = state->state.crtc_h; for (i = 0; i < state->format->planes; ++i) { struct drm_gem_cma_object *gem; gem = drm_fb_cma_get_gem_obj(fb, i); - paddr[i] = gem->paddr + fb->offsets[i]; + cfg.mem[i] = gem->paddr + fb->offsets[i]; } for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) { if (formats_kms[i] == state->format->fourcc) { - pixelformat = formats_v4l2[i]; + cfg.pixelformat = formats_v4l2[i]; break; } } - WARN_ON(!pixelformat); - - vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat, - fb->pitches[0], paddr, &src, &dst); + vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg); } static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, @@ -220,8 +219,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane, if (plane->state->crtc) rcar_du_vsp_plane_setup(rplane); else - vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0, - NULL, NULL); + vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL); } static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = { @@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane) return; state->alpha = 255; + state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; plane->state = &state->state; plane->state->plane = plane; @@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane, if (property == rcdu->props.alpha) rstate->alpha = val; + else if (property == rcdu->props.zpos) + rstate->zpos = val; else return -EINVAL; @@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane, if (property == rcdu->props.alpha) *val = rstate->alpha; + else if (property == rcdu->props.zpos) + *val = rstate->zpos; else return -EINVAL; @@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp) drm_object_attach_property(&plane->plane.base, rcdu->props.alpha, 255); + drm_object_attach_property(&plane->plane.base, + rcdu->props.zpos, 1); } return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index df3bf3805c69..510dcc9c6816 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p) * @state: base DRM plane state * @format: information about the pixel format used by the plane * @alpha: value of the plane alpha property + * @zpos: value of the plane zpos property */ struct rcar_du_vsp_plane_state { struct drm_plane_state state; @@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state { const struct rcar_du_format_info *format; unsigned int alpha; + unsigned int zpos; }; static inline struct rcar_du_vsp_plane_state * diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 880c40b23f66..4ea475775d58 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -126,7 +126,7 @@ struct sur40_image_header { #define VIDEO_PACKET_SIZE 16384 /* polling interval (ms) */ -#define POLL_INTERVAL 4 +#define POLL_INTERVAL 1 /* maximum number of contacts FIXME: this is a guess? */ #define MAX_CONTACTS 64 @@ -151,7 +151,6 @@ struct sur40_state { struct mutex lock; struct vb2_queue queue; - struct vb2_alloc_ctx *alloc_ctx; struct list_head buf_list; spinlock_t qlock; int sequence; @@ -448,7 +447,7 @@ static void sur40_process_video(struct sur40_state *sur40) /* return error if streaming was stopped in the meantime */ if (sur40->sequence == -1) - goto err_poll; + return; /* mark as finished */ new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); @@ -580,19 +579,13 @@ static int sur40_probe(struct usb_interface *interface, sur40->queue = sur40_queue; sur40->queue.drv_priv = sur40; sur40->queue.lock = &sur40->lock; + sur40->queue.dev = sur40->dev; /* initialize the queue */ error = vb2_queue_init(&sur40->queue); if (error) goto err_unreg_v4l2; - sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); - if (IS_ERR(sur40->alloc_ctx)) { - dev_err(sur40->dev, "Can't allocate buffer context"); - error = PTR_ERR(sur40->alloc_ctx); - goto err_unreg_v4l2; - } - sur40->vdev = sur40_video_device; sur40->vdev.v4l2_dev = &sur40->v4l2; sur40->vdev.lock = &sur40->lock; @@ -633,7 +626,6 @@ static void sur40_disconnect(struct usb_interface *interface) video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); @@ -653,13 +645,10 @@ static void sur40_disconnect(struct usb_interface *interface) */ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { - struct sur40_state *sur40 = vb2_get_drv_priv(q); - if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; - alloc_ctxs[0] = sur40->alloc_ctx; if (*nplanes) return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; @@ -736,6 +725,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) static void sur40_stop_streaming(struct vb2_queue *vq) { struct sur40_state *sur40 = vb2_get_drv_priv(vq); + vb2_wait_for_all_buffers(vq); sur40->sequence = -1; /* Release all active buffers */ @@ -793,7 +783,6 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, { if (f->index != 0) return -EINVAL; - strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); f->pixelformat = V4L2_PIX_FMT_GREY; f->flags = 0; return 0; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index a8518fb3bca7..962f2a9a6614 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT Say Y when you have a TV or an IR device. +config MEDIA_CEC_EDID + bool + # # Media controller # Selectable only for webcam/grabbers, as other drivers don't use it diff --git a/drivers/media/Makefile b/drivers/media/Makefile index e608bbce0c35..081a7866fd44 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,6 +2,10 @@ # Makefile for the kernel multimedia device drivers. # +ifeq ($(CONFIG_MEDIA_CEC_EDID),y) + obj-$(CONFIG_MEDIA_SUPPORT) += cec-edid.o +endif + media-objs := media-device.o media-devnode.o media-entity.o # diff --git a/drivers/media/cec-edid.c b/drivers/media/cec-edid.c new file mode 100644 index 000000000000..70018247bdda --- /dev/null +++ b/drivers/media/cec-edid.c @@ -0,0 +1,168 @@ +/* + * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions + * + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +/* + * This EDID is expected to be a CEA-861 compliant, which means that there are + * at least two blocks and one or more of the extensions blocks are CEA-861 + * blocks. + * + * The returned location is guaranteed to be < size - 1. + */ +static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size) +{ + unsigned int blocks = size / 128; + unsigned int block; + u8 d; + + /* Sanity check: at least 2 blocks and a multiple of the block size */ + if (blocks < 2 || size % 128) + return 0; + + /* + * If there are fewer extension blocks than the size, then update + * 'blocks'. It is allowed to have more extension blocks than the size, + * since some hardware can only read e.g. 256 bytes of the EDID, even + * though more blocks are present. The first CEA-861 extension block + * should normally be in block 1 anyway. + */ + if (edid[0x7e] + 1 < blocks) + blocks = edid[0x7e] + 1; + + for (block = 1; block < blocks; block++) { + unsigned int offset = block * 128; + + /* Skip any non-CEA-861 extension blocks */ + if (edid[offset] != 0x02 || edid[offset + 1] != 0x03) + continue; + + /* search Vendor Specific Data Block (tag 3) */ + d = edid[offset + 2] & 0x7f; + /* Check if there are Data Blocks */ + if (d <= 4) + continue; + if (d > 4) { + unsigned int i = offset + 4; + unsigned int end = offset + d; + + /* Note: 'end' is always < 'size' */ + do { + u8 tag = edid[i] >> 5; + u8 len = edid[i] & 0x1f; + + if (tag == 3 && len >= 5 && i + len <= end) + return i + 4; + i += len + 1; + } while (i < end); + } + } + return 0; +} + +u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size, + unsigned int *offset) +{ + unsigned int loc = cec_get_edid_spa_location(edid, size); + + if (offset) + *offset = loc; + if (loc == 0) + return CEC_PHYS_ADDR_INVALID; + return (edid[loc] << 8) | edid[loc + 1]; +} +EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr); + +void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr) +{ + unsigned int loc = cec_get_edid_spa_location(edid, size); + u8 sum = 0; + unsigned int i; + + if (loc == 0) + return; + edid[loc] = phys_addr >> 8; + edid[loc + 1] = phys_addr & 0xff; + loc &= ~0x7f; + + /* update the checksum */ + for (i = loc; i < loc + 127; i++) + sum += edid[i]; + edid[i] = 256 - sum; +} +EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr); + +u16 cec_phys_addr_for_input(u16 phys_addr, u8 input) +{ + /* Check if input is sane */ + if (WARN_ON(input == 0 || input > 0xf)) + return CEC_PHYS_ADDR_INVALID; + + if (phys_addr == 0) + return input << 12; + + if ((phys_addr & 0x0fff) == 0) + return phys_addr | (input << 8); + + if ((phys_addr & 0x00ff) == 0) + return phys_addr | (input << 4); + + if ((phys_addr & 0x000f) == 0) + return phys_addr | input; + + /* + * All nibbles are used so no valid physical addresses can be assigned + * to the input. + */ + return CEC_PHYS_ADDR_INVALID; +} +EXPORT_SYMBOL_GPL(cec_phys_addr_for_input); + +int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) +{ + int i; + + if (parent) + *parent = phys_addr; + if (port) + *port = 0; + if (phys_addr == CEC_PHYS_ADDR_INVALID) + return 0; + for (i = 0; i < 16; i += 4) + if (phys_addr & (0xf << i)) + break; + if (i == 16) + return 0; + if (parent) + *parent = phys_addr & (0xfff0 << i); + if (port) + *port = (phys_addr >> i) & 0xf; + for (i += 4; i < 16; i += 4) + if ((phys_addr & (0xf << i)) == 0) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(cec_phys_addr_validate); + +MODULE_AUTHOR("Hans Verkuil "); +MODULE_DESCRIPTION("CEC EDID helper functions"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index cf1dadd0be9e..3ec3cebe62b9 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -777,7 +777,7 @@ static void precalculate_color(struct tpg_data *tpg, int k) * Remember that r, g and b are still in the 0 - 0xff0 range. */ if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED && - tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) { + tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL && !tpg->is_yuv) { /* * Convert from full range (which is what r, g and b are) * to limited range (which is the 'real' RGB range), which @@ -787,7 +787,7 @@ static void precalculate_color(struct tpg_data *tpg, int k) g = (g * 219) / 255 + (16 << 4); b = (b * 219) / 255 + (16 << 4); } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED && - tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) { + tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED && !tpg->is_yuv) { /* * Clamp r, g and b to the limited range and convert to full * range since that's what we deliver. diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h index 6d3b95b8939d..7f1dffef4353 100644 --- a/drivers/media/dvb-core/demux.h +++ b/drivers/media/dvb-core/demux.h @@ -143,7 +143,7 @@ struct dmx_ts_feed { int type, enum dmx_ts_pes pes_type, size_t circular_buffer_size, - struct timespec timeout); + ktime_t timeout); int (*start_filtering)(struct dmx_ts_feed *feed); int (*stop_filtering)(struct dmx_ts_feed *feed); }; diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index a168cbe1c998..7b67e1dd97fd 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -556,7 +556,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, struct dmxdev_filter *filter, struct dmxdev_feed *feed) { - struct timespec timeout = { 0 }; + ktime_t timeout = ktime_set(0, 0); struct dmx_pes_filter_params *para = &filter->params.pes; dmx_output_t otype; int ret; diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index f82cd1ff4f3a..b5b5b195ea7f 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -123,6 +123,7 @@ struct dvb_ca_slot { /* Private CA-interface information */ struct dvb_ca_private { + struct kref refcount; /* pointer back to the public data structure */ struct dvb_ca_en50221 *pub; @@ -161,6 +162,34 @@ struct dvb_ca_private { struct mutex ioctl_mutex; }; +static void dvb_ca_private_free(struct dvb_ca_private *ca) +{ + unsigned int i; + + dvb_unregister_device(ca->dvbdev); + for (i = 0; i < ca->slot_count; i++) + vfree(ca->slot_info[i].rx_buffer.data); + + kfree(ca->slot_info); + kfree(ca); +} + +static void dvb_ca_private_release(struct kref *ref) +{ + struct dvb_ca_private *ca = container_of(ref, struct dvb_ca_private, refcount); + dvb_ca_private_free(ca); +} + +static void dvb_ca_private_get(struct dvb_ca_private *ca) +{ + kref_get(&ca->refcount); +} + +static void dvb_ca_private_put(struct dvb_ca_private *ca) +{ + kref_put(&ca->refcount, dvb_ca_private_release); +} + static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); @@ -1558,6 +1587,8 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_wakeup(ca); + dvb_ca_private_get(ca); + return 0; } @@ -1586,6 +1617,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) module_put(ca->pub->owner); + dvb_ca_private_put(ca); + return err; } @@ -1681,6 +1714,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, ret = -ENOMEM; goto exit; } + kref_init(&ca->refcount); ca->pub = pubca; ca->flags = flags; ca->slot_count = slot_count; @@ -1759,10 +1793,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) for (i = 0; i < ca->slot_count; i++) { dvb_ca_en50221_slot_shutdown(ca, i); - vfree(ca->slot_info[i].rx_buffer.data); } - kfree(ca->slot_info); - dvb_unregister_device(ca->dvbdev); - kfree(ca); + dvb_ca_private_put(ca); pubca->private = NULL; } diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 0cc5e935166c..a0cf7b0d03e8 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -398,28 +398,23 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) int dvr_done = 0; if (dvb_demux_speedcheck) { - struct timespec cur_time, delta_time; + ktime_t cur_time; u64 speed_bytes, speed_timedelta; demux->speed_pkts_cnt++; /* show speed every SPEED_PKTS_INTERVAL packets */ if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { - cur_time = current_kernel_time(); + cur_time = ktime_get(); - if (demux->speed_last_time.tv_sec != 0 && - demux->speed_last_time.tv_nsec != 0) { - delta_time = timespec_sub(cur_time, - demux->speed_last_time); + if (ktime_to_ns(demux->speed_last_time) != 0) { speed_bytes = (u64)demux->speed_pkts_cnt * 188 * 8; /* convert to 1024 basis */ speed_bytes = 1000 * div64_u64(speed_bytes, 1024); - speed_timedelta = - (u64)timespec_to_ns(&delta_time); - speed_timedelta = div64_u64(speed_timedelta, - 1000000); /* nsec -> usec */ + speed_timedelta = ktime_ms_delta(cur_time, + demux->speed_last_time); printk(KERN_INFO "TS speed %llu Kbits/sec \n", div64_u64(speed_bytes, speed_timedelta)); @@ -666,7 +661,7 @@ static void dvb_demux_feed_del(struct dvb_demux_feed *feed) static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, enum dmx_ts_pes pes_type, - size_t circular_buffer_size, struct timespec timeout) + size_t circular_buffer_size, ktime_t timeout) { struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; struct dvb_demux *demux = feed->demux; diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h index ae7fc33c3231..5ed3cab4ad28 100644 --- a/drivers/media/dvb-core/dvb_demux.h +++ b/drivers/media/dvb-core/dvb_demux.h @@ -83,7 +83,7 @@ struct dvb_demux_feed { u8 *buffer; int buffer_size; - struct timespec timeout; + ktime_t timeout; struct dvb_demux_filter *filter; int ts_type; @@ -134,7 +134,7 @@ struct dvb_demux { uint8_t *cnt_storage; /* for TS continuity check */ - struct timespec speed_last_time; /* for TS speed check */ + ktime_t speed_last_time; /* for TS speed check */ uint32_t speed_pkts_cnt; /* for TS speed check */ }; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index c0142614c408..be99c8dbc5f8 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -99,6 +99,7 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open( static DEFINE_MUTEX(frontend_mutex); struct dvb_frontend_private { + struct kref refcount; /* thread/frontend values */ struct dvb_device *dvbdev; @@ -137,6 +138,23 @@ struct dvb_frontend_private { #endif }; +static void dvb_frontend_private_free(struct kref *ref) +{ + struct dvb_frontend_private *fepriv = + container_of(ref, struct dvb_frontend_private, refcount); + kfree(fepriv); +} + +static void dvb_frontend_private_put(struct dvb_frontend_private *fepriv) +{ + kref_put(&fepriv->refcount, dvb_frontend_private_free); +} + +static void dvb_frontend_private_get(struct dvb_frontend_private *fepriv) +{ + kref_get(&fepriv->refcount); +} + static void dvb_frontend_wakeup(struct dvb_frontend *fe); static int dtv_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *c, @@ -2543,6 +2561,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) fepriv->events.eventr = fepriv->events.eventw = 0; } + dvb_frontend_private_get(fepriv); + if (adapter->mfe_shared) mutex_unlock (&adapter->mfe_lock); return ret; @@ -2591,6 +2611,8 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) fe->ops.ts_bus_ctrl(fe, 0); } + dvb_frontend_private_put(fepriv); + return ret; } @@ -2679,6 +2701,8 @@ int dvb_register_frontend(struct dvb_adapter* dvb, } fepriv = fe->frontend_priv; + kref_init(&fepriv->refcount); + sema_init(&fepriv->sem, 1); init_waitqueue_head (&fepriv->wait_queue); init_waitqueue_head (&fepriv->events.wait_queue); @@ -2713,18 +2737,11 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) mutex_lock(&frontend_mutex); dvb_frontend_stop (fe); - mutex_unlock(&frontend_mutex); - - if (fepriv->dvbdev->users < -1) - wait_event(fepriv->dvbdev->wait_queue, - fepriv->dvbdev->users==-1); - - mutex_lock(&frontend_mutex); dvb_unregister_device (fepriv->dvbdev); /* fe is invalid now */ - kfree(fepriv); mutex_unlock(&frontend_mutex); + dvb_frontend_private_put(fepriv); return 0; } EXPORT_SYMBOL(dvb_unregister_frontend); diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index ce6a711b42d4..9914f69a4a02 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -997,7 +997,7 @@ static int dvb_net_feed_start(struct net_device *dev) netdev_dbg(dev, "start filtering\n"); priv->secfeed->start_filtering(priv->secfeed); } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { - struct timespec timeout = { 0, 10000000 }; // 10 msec + ktime_t timeout = ns_to_ktime(10 * NSEC_PER_MSEC); /* we have payloads encapsulated in TS */ netdev_dbg(dev, "alloc tsfeed\n"); diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c index 1100e98a7b1d..7df7fb3738a0 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb-core/dvb_ringbuffer.c @@ -55,7 +55,13 @@ void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) { - return (rbuf->pread==rbuf->pwrite); + /* smp_load_acquire() to load write pointer on reader side + * this pairs with smp_store_release() in dvb_ringbuffer_write(), + * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() + * + * for memory barriers also see Documentation/circular-buffers.txt + */ + return (rbuf->pread == smp_load_acquire(&rbuf->pwrite)); } @@ -64,7 +70,12 @@ ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) { ssize_t free; - free = rbuf->pread - rbuf->pwrite; + /* ACCESS_ONCE() to load read pointer on writer side + * this pairs with smp_store_release() in dvb_ringbuffer_read(), + * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(), + * or dvb_ringbuffer_reset() + */ + free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite; if (free <= 0) free += rbuf->size; return free-1; @@ -76,7 +87,11 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) { ssize_t avail; - avail = rbuf->pwrite - rbuf->pread; + /* smp_load_acquire() to load write pointer on reader side + * this pairs with smp_store_release() in dvb_ringbuffer_write(), + * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() + */ + avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread; if (avail < 0) avail += rbuf->size; return avail; @@ -86,14 +101,25 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) { - rbuf->pread = rbuf->pwrite; + /* dvb_ringbuffer_flush() counts as read operation + * smp_load_acquire() to load write pointer + * smp_store_release() to update read pointer, this ensures that the + * correct pointer is visible for subsequent dvb_ringbuffer_free() + * calls on other cpu cores + */ + smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite)); rbuf->error = 0; } EXPORT_SYMBOL(dvb_ringbuffer_flush); void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) { - rbuf->pread = rbuf->pwrite = 0; + /* dvb_ringbuffer_reset() counts as read and write operation + * smp_store_release() to update read pointer + */ + smp_store_release(&rbuf->pread, 0); + /* smp_store_release() to update write pointer */ + smp_store_release(&rbuf->pwrite, 0); rbuf->error = 0; } @@ -119,12 +145,17 @@ ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, si return -EFAULT; buf += split; todo -= split; - rbuf->pread = 0; + /* smp_store_release() for read pointer update to ensure + * that buf is not overwritten until read is complete, + * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() + */ + smp_store_release(&rbuf->pread, 0); } if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) return -EFAULT; - rbuf->pread = (rbuf->pread + todo) % rbuf->size; + /* smp_store_release() to update read pointer, see above */ + smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); return len; } @@ -139,11 +170,16 @@ void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) memcpy(buf, rbuf->data+rbuf->pread, split); buf += split; todo -= split; - rbuf->pread = 0; + /* smp_store_release() for read pointer update to ensure + * that buf is not overwritten until read is complete, + * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() + */ + smp_store_release(&rbuf->pread, 0); } memcpy(buf, rbuf->data+rbuf->pread, todo); - rbuf->pread = (rbuf->pread + todo) % rbuf->size; + /* smp_store_release() to update read pointer, see above */ + smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); } @@ -158,10 +194,16 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t memcpy(rbuf->data+rbuf->pwrite, buf, split); buf += split; todo -= split; - rbuf->pwrite = 0; + /* smp_store_release() for write pointer update to ensure that + * written data is visible on other cpu cores before the pointer + * update, this pairs with smp_load_acquire() in + * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() + */ + smp_store_release(&rbuf->pwrite, 0); } memcpy(rbuf->data+rbuf->pwrite, buf, todo); - rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; + /* smp_store_release() for write pointer update, see above */ + smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); return len; } @@ -181,12 +223,18 @@ ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, return len - todo; buf += split; todo -= split; - rbuf->pwrite = 0; + /* smp_store_release() for write pointer update to ensure that + * written data is visible on other cpu cores before the pointer + * update, this pairs with smp_load_acquire() in + * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() + */ + smp_store_release(&rbuf->pwrite, 0); } status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); if (status) return len - todo; - rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; + /* smp_store_release() for write pointer update, see above */ + smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); return len; } diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a82f77c49bd5..c645aa81f423 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -73,6 +73,14 @@ config DVB_SI2165 Say Y when you want to support this frontend. +config DVB_MN88472 + tristate "Panasonic MN88472" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + config DVB_MN88473 tristate "Panasonic MN88473" depends on DVB_CORE && I2C @@ -853,6 +861,13 @@ config DVB_ASCOT2E help Say Y when you want to support this frontend. +config DVB_HELENE + tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index eb7191f4219d..e90165ad361b 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_MN88472) += mn88472.o obj-$(CONFIG_DVB_MN88473) += mn88473.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o @@ -123,3 +124,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o +obj-$(CONFIG_DVB_HELENE) += helene.o diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index efebe5ce2429..9a8157a5f49d 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -41,7 +41,6 @@ struct af9033_dev { u64 post_bit_count; u64 error_block_count; u64 total_block_count; - struct delayed_work stat_work; }; /* write multiple registers */ @@ -468,8 +467,6 @@ static int af9033_init(struct dvb_frontend *fe) c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; c->post_bit_error.len = 1; c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - /* start statistics polling */ - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); return 0; @@ -485,9 +482,6 @@ static int af9033_sleep(struct dvb_frontend *fe) int ret, i; u8 tmp; - /* stop statistics polling */ - cancel_delayed_work_sync(&dev->stat_work); - ret = af9033_wr_reg(dev, 0x80004c, 1); if (ret < 0) goto err; @@ -821,36 +815,39 @@ static int af9033_get_frontend(struct dvb_frontend *fe, static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct af9033_dev *dev = fe->demodulator_priv; - int ret; - u8 tmp; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, tmp = 0; + u8 u8tmp, buf[7]; + + dev_dbg(&dev->client->dev, "\n"); *status = 0; /* radio channel status, 0=no result, 1=has signal, 2=no signal */ - ret = af9033_rd_reg(dev, 0x800047, &tmp); + ret = af9033_rd_reg(dev, 0x800047, &u8tmp); if (ret < 0) goto err; /* has signal */ - if (tmp == 0x01) + if (u8tmp == 0x01) *status |= FE_HAS_SIGNAL; - if (tmp != 0x02) { + if (u8tmp != 0x02) { /* TPS lock */ - ret = af9033_rd_reg_mask(dev, 0x80f5a9, &tmp, 0x01); + ret = af9033_rd_reg_mask(dev, 0x80f5a9, &u8tmp, 0x01); if (ret < 0) goto err; - if (tmp) + if (u8tmp) *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI; /* full lock */ - ret = af9033_rd_reg_mask(dev, 0x80f999, &tmp, 0x01); + ret = af9033_rd_reg_mask(dev, 0x80f999, &u8tmp, 0x01); if (ret < 0) goto err; - if (tmp) + if (u8tmp) *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; @@ -858,6 +855,148 @@ static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status) dev->fe_status = *status; + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + if (dev->is_af9035) { + ret = af9033_rd_reg(dev, 0x80004a, &u8tmp); + if (ret) + goto err; + tmp = -u8tmp * 1000; + } else { + ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp); + if (ret) + goto err; + tmp = (u8tmp - 100) * 1000; + } + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = tmp; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + u32 snr_val, snr_lut_size; + const struct val_snr *snr_lut = NULL; + + /* read value */ + ret = af9033_rd_regs(dev, 0x80002c, buf, 3); + if (ret) + goto err; + + snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); + + /* read superframe number */ + ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp); + if (ret) + goto err; + + if (u8tmp) + snr_val /= u8tmp; + + /* read current transmission mode */ + ret = af9033_rd_reg(dev, 0x80f900, &u8tmp); + if (ret) + goto err; + + switch ((u8tmp >> 0) & 3) { + case 0: + snr_val *= 4; + break; + case 1: + snr_val *= 1; + break; + case 2: + snr_val *= 2; + break; + default: + snr_val *= 0; + break; + } + + /* read current modulation */ + ret = af9033_rd_reg(dev, 0x80f903, &u8tmp); + if (ret) + goto err; + + switch ((u8tmp >> 0) & 3) { + case 0: + snr_lut_size = ARRAY_SIZE(qpsk_snr_lut); + snr_lut = qpsk_snr_lut; + break; + case 1: + snr_lut_size = ARRAY_SIZE(qam16_snr_lut); + snr_lut = qam16_snr_lut; + break; + case 2: + snr_lut_size = ARRAY_SIZE(qam64_snr_lut); + snr_lut = qam64_snr_lut; + break; + default: + snr_lut_size = 0; + tmp = 0; + break; + } + + for (i = 0; i < snr_lut_size; i++) { + tmp = snr_lut[i].snr * 1000; + if (snr_val < snr_lut[i].val) + break; + } + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = tmp; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* outer FEC, 204 byte packets */ + u16 abort_packet_count, rsd_packet_count; + /* inner FEC, bits */ + u32 rsd_bit_err_count; + + /* + * Packet count used for measurement is 10000 + * (rsd_packet_count). Maybe it should be increased? + */ + + ret = af9033_rd_regs(dev, 0x800032, buf, 7); + if (ret) + goto err; + + abort_packet_count = (buf[1] << 8) | (buf[0] << 0); + rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2]; + rsd_packet_count = (buf[6] << 8) | (buf[5] << 0); + + dev->error_block_count += abort_packet_count; + dev->total_block_count += rsd_packet_count; + dev->post_bit_error += rsd_bit_err_count; + dev->post_bit_count += rsd_packet_count * 204 * 8; + + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue = dev->total_block_count; + + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->error_block_count; + + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + } + return 0; err: @@ -1059,159 +1198,6 @@ static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, return ret; } -static void af9033_stat_work(struct work_struct *work) -{ - struct af9033_dev *dev = container_of(work, struct af9033_dev, stat_work.work); - struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; - int ret, tmp, i, len; - u8 u8tmp, buf[7]; - - dev_dbg(&dev->client->dev, "\n"); - - /* signal strength */ - if (dev->fe_status & FE_HAS_SIGNAL) { - if (dev->is_af9035) { - ret = af9033_rd_reg(dev, 0x80004a, &u8tmp); - tmp = -u8tmp * 1000; - } else { - ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp); - tmp = (u8tmp - 100) * 1000; - } - if (ret) - goto err; - - c->strength.len = 1; - c->strength.stat[0].scale = FE_SCALE_DECIBEL; - c->strength.stat[0].svalue = tmp; - } else { - c->strength.len = 1; - c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - } - - /* CNR */ - if (dev->fe_status & FE_HAS_VITERBI) { - u32 snr_val; - const struct val_snr *snr_lut; - - /* read value */ - ret = af9033_rd_regs(dev, 0x80002c, buf, 3); - if (ret) - goto err; - - snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); - - /* read superframe number */ - ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp); - if (ret) - goto err; - - if (u8tmp) - snr_val /= u8tmp; - - /* read current transmission mode */ - ret = af9033_rd_reg(dev, 0x80f900, &u8tmp); - if (ret) - goto err; - - switch ((u8tmp >> 0) & 3) { - case 0: - snr_val *= 4; - break; - case 1: - snr_val *= 1; - break; - case 2: - snr_val *= 2; - break; - default: - goto err_schedule_delayed_work; - } - - /* read current modulation */ - ret = af9033_rd_reg(dev, 0x80f903, &u8tmp); - if (ret) - goto err; - - switch ((u8tmp >> 0) & 3) { - case 0: - len = ARRAY_SIZE(qpsk_snr_lut); - snr_lut = qpsk_snr_lut; - break; - case 1: - len = ARRAY_SIZE(qam16_snr_lut); - snr_lut = qam16_snr_lut; - break; - case 2: - len = ARRAY_SIZE(qam64_snr_lut); - snr_lut = qam64_snr_lut; - break; - default: - goto err_schedule_delayed_work; - } - - for (i = 0; i < len; i++) { - tmp = snr_lut[i].snr * 1000; - if (snr_val < snr_lut[i].val) - break; - } - - c->cnr.len = 1; - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = tmp; - } else { - c->cnr.len = 1; - c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - } - - /* UCB/PER/BER */ - if (dev->fe_status & FE_HAS_LOCK) { - /* outer FEC, 204 byte packets */ - u16 abort_packet_count, rsd_packet_count; - /* inner FEC, bits */ - u32 rsd_bit_err_count; - - /* - * Packet count used for measurement is 10000 - * (rsd_packet_count). Maybe it should be increased? - */ - - ret = af9033_rd_regs(dev, 0x800032, buf, 7); - if (ret) - goto err; - - abort_packet_count = (buf[1] << 8) | (buf[0] << 0); - rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2]; - rsd_packet_count = (buf[6] << 8) | (buf[5] << 0); - - dev->error_block_count += abort_packet_count; - dev->total_block_count += rsd_packet_count; - dev->post_bit_error += rsd_bit_err_count; - dev->post_bit_count += rsd_packet_count * 204 * 8; - - c->block_count.len = 1; - c->block_count.stat[0].scale = FE_SCALE_COUNTER; - c->block_count.stat[0].uvalue = dev->total_block_count; - - c->block_error.len = 1; - c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue = dev->error_block_count; - - c->post_bit_count.len = 1; - c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[0].uvalue = dev->post_bit_count; - - c->post_bit_error.len = 1; - c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[0].uvalue = dev->post_bit_error; - } - -err_schedule_delayed_work: - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); - return; -err: - dev_dbg(&dev->client->dev, "failed=%d\n", ret); -} - static struct dvb_frontend_ops af9033_ops = { .delsys = { SYS_DVBT }, .info = { @@ -1272,7 +1258,6 @@ static int af9033_probe(struct i2c_client *client, /* setup the state */ dev->client = client; - INIT_DELAYED_WORK(&dev->stat_work, af9033_stat_work); memcpy(&dev->cfg, cfg, sizeof(struct af9033_config)); if (dev->cfg.clock != 12000000) { @@ -1372,9 +1357,6 @@ static int af9033_remove(struct i2c_client *client) dev_dbg(&dev->client->dev, "\n"); - /* stop statistics polling */ - cancel_delayed_work_sync(&dev->stat_work); - dev->fe.ops.release = NULL; dev->fe.demodulator_priv = NULL; kfree(dev); @@ -1391,6 +1373,7 @@ MODULE_DEVICE_TABLE(i2c, af9033_id_table); static struct i2c_driver af9033_driver = { .driver = { .name = "af9033", + .suppress_bind_attrs = true, }, .probe = af9033_probe, .remove = af9033_remove, diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index f770f6a2c987..8cc8c4597b6a 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -132,7 +132,7 @@ static int ascot2e_write_regs(struct ascot2e_priv *priv, } }; - if (len + 1 >= sizeof(buf)) { + if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 900186ba8e62..09c39346167f 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -1,7 +1,9 @@ /* * cxd2841er.c * - * Sony CXD2441ER digital demodulator driver + * Sony digital demodulator driver for + * CXD2841ER - DVB-S/S2/T/T2/C/C2 + * CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S * * Copyright 2012 Sony Corporation * Copyright (C) 2014 NetUP Inc. @@ -34,6 +36,16 @@ #include "cxd2841er_priv.h" #define MAX_WRITE_REGSIZE 16 +#define LOG2_E_100X 144 + +/* DVB-C constellation */ +enum sony_dvbc_constellation_t { + SONY_DVBC_CONSTELLATION_16QAM, + SONY_DVBC_CONSTELLATION_32QAM, + SONY_DVBC_CONSTELLATION_64QAM, + SONY_DVBC_CONSTELLATION_128QAM, + SONY_DVBC_CONSTELLATION_256QAM +}; enum cxd2841er_state { STATE_SHUTDOWN = 0, @@ -51,6 +63,8 @@ struct cxd2841er_priv { const struct cxd2841er_config *config; enum cxd2841er_state state; u8 system; + enum cxd2841er_xtal xtal; + enum fe_caps caps; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -188,6 +202,9 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = { }; #define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5)) +#define MAKE_IFFREQ_CONFIG_XTAL(xtal, iffreq) ((xtal == SONY_XTAL_24000) ? \ + (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \ + (u32)(((iffreq)/41.0)*16777216.0 + 0.5)) static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, u8 addr, u8 reg, u8 write, @@ -217,7 +234,7 @@ static int cxd2841er_write_regs(struct cxd2841er_priv *priv, }; if (len + 1 >= sizeof(buf)) { - dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; } @@ -282,6 +299,7 @@ static int cxd2841er_read_regs(struct cxd2841er_priv *priv, KBUILD_MODNAME, ret, i2c_addr, reg); return ret; } + cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len); return 0; } @@ -427,6 +445,15 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u32 bandwidth); +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv); + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv); + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv); + static int cxd2841er_retune_active(struct cxd2841er_priv *priv, struct dtv_frontend_properties *p) { @@ -454,7 +481,13 @@ static int cxd2841er_retune_active(struct cxd2841er_priv *priv, priv, p->bandwidth_hz); case SYS_DVBC_ANNEX_A: return cxd2841er_sleep_tc_to_active_c_band( - priv, 8000000); + priv, p->bandwidth_hz); + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + cxd2841er_sleep_tc_to_shutdown(priv); + cxd2841er_shutdown_to_sleep_tc(priv); + return cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); } } dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", @@ -669,6 +702,45 @@ static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) return 0; } +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + + /* TODO: Cancel demod parameter */ + + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) { dev_dbg(&priv->i2c->dev, "%s()\n", __func__); @@ -686,8 +758,25 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03); + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + /* Set demod mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); /* Clear demod SW reset */ @@ -712,6 +801,8 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) { + u8 data = 0; + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); if (priv->state != STATE_SHUTDOWN) { dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", @@ -727,9 +818,24 @@ static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ + /* Select ADC clock mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + data = 0x0; + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x3; + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x1; + break; + } + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data); /* Clear demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); usleep_range(1000, 2000); @@ -809,11 +915,14 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) { - u8 chip_id; + u8 chip_id = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); - cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); - cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id); + return chip_id; } @@ -896,6 +1005,25 @@ static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) return 0; } +static int cxd2841er_read_status_i(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + dev_dbg(&priv->i2c->dev, + "%s(): lock=0x%x\n", __func__, data); + *sync = ((data & 0x02) ? 1 : 0); + *tslock = ((data & 0x01) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + return 0; +} + static int cxd2841er_read_status_tc(struct dvb_frontend *fe, enum fe_status *status) { @@ -921,6 +1049,20 @@ static int cxd2841er_read_status_tc(struct dvb_frontend *fe, FE_HAS_SYNC; if (tslock) *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_ISDBT) { + ret = cxd2841er_read_status_i( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; } else if (priv->system == SYS_DVBC_ANNEX_A) { ret = cxd2841er_read_status_c(priv, &tslock); if (ret) @@ -997,6 +1139,76 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_ISDBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + + switch (bandwidth) { + case 6000000: + *offset = -1 * ((*offset) * 8/264); + break; + case 7000000: + *offset = -1 * ((*offset) * 8/231); + break; + case 8000000: + *offset = -1 * ((*offset) * 8/198); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n", + __func__, bandwidth, *offset); + + return 0; +} + +static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + *offset *= (bandwidth / 1000000); + *offset /= 235; + return 0; +} + static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, u32 bandwidth, int *offset) { @@ -1060,6 +1272,24 @@ static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_read_packet_errors_c( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data)); + if (data[2] & 0x01) + *penum = ((u32)data[0] << 8) | (u32)data[1]; + return 0; +} + static int cxd2841er_read_packet_errors_t( struct cxd2841er_priv *priv, u32 *penum) { @@ -1096,11 +1326,85 @@ static int cxd2841er_read_packet_errors_t2( return 0; } -static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) +static int cxd2841er_read_packet_errors_i( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[2]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1); + + if (!(data[0] & 0x01)) + return 0; + + /* Layer A */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data)); + *penum = ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer B */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer C */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + return 0; +} + +static int cxd2841er_read_ber_c(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[3]; + u32 bit_err, period_exp; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x62, data, sizeof(data)); + if (!(data[0] & 0x80)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return -EINVAL; + } + bit_err = ((u32)(data[0] & 0x3f) << 16) | + ((u32)data[1] << 8) | + (u32)data[2]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x60, data); + period_exp = data[0] & 0x1f; + + if ((period_exp <= 11) && (bit_err > (1 << period_exp) * 204 * 8)) { + dev_dbg(&priv->i2c->dev, + "%s(): period_exp(%u) or bit_err(%u) not in range. no valid BER data\n", + __func__, period_exp, bit_err); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, + "%s(): period_exp(%u) or bit_err(%u) count=%d\n", + __func__, period_exp, bit_err, + ((1 << period_exp) * 204 * 8)); + + *bit_error = bit_err; + *bit_count = ((1 << period_exp) * 204 * 8); + + return 0; +} + +static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) { u8 data[11]; - u32 bit_error, bit_count; - u32 temp_q, temp_r; /* Set SLV-T Bank : 0xA0 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); @@ -1116,40 +1420,30 @@ static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) */ cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11); if (data[0] & 0x01) { - bit_error = ((u32)(data[1] & 0x3F) << 16) | - ((u32)(data[2] & 0xFF) << 8) | - (u32)(data[3] & 0xFF); - bit_count = ((u32)(data[8] & 0x3F) << 16) | - ((u32)(data[9] & 0xFF) << 8) | - (u32)(data[10] & 0xFF); - /* - * BER = bitError / bitCount - * = (bitError * 10^7) / bitCount - * = ((bitError * 625 * 125 * 128) / bitCount - */ - if ((bit_count == 0) || (bit_error > bit_count)) { + *bit_error = ((u32)(data[1] & 0x3F) << 16) | + ((u32)(data[2] & 0xFF) << 8) | + (u32)(data[3] & 0xFF); + *bit_count = ((u32)(data[8] & 0x3F) << 16) | + ((u32)(data[9] & 0xFF) << 8) | + (u32)(data[10] & 0xFF); + if ((*bit_count == 0) || (*bit_error > *bit_count)) { dev_dbg(&priv->i2c->dev, "%s(): invalid bit_error %d, bit_count %d\n", - __func__, bit_error, bit_count); - return 0; + __func__, *bit_error, *bit_count); + return -EINVAL; } - temp_q = div_u64_rem(10000000ULL * bit_error, - bit_count, &temp_r); - if (bit_count != 1 && temp_r >= bit_count / 2) - temp_q++; - return temp_q; + return 0; } dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); - return 0; + return -EINVAL; } -static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv) +static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) { u8 data[5]; - u32 bit_error, period; - u32 temp_q, temp_r; - u32 result = 0; + u32 period; /* Set SLV-T Bank : 0xB2 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2); @@ -1164,10 +1458,10 @@ static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv) cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5); if (data[0] & 0x01) { /* Bit error count */ - bit_error = ((u32)(data[1] & 0x0F) << 24) | - ((u32)(data[2] & 0xFF) << 16) | - ((u32)(data[3] & 0xFF) << 8) | - (u32)(data[4] & 0xFF); + *bit_error = ((u32)(data[1] & 0x0F) << 24) | + ((u32)(data[2] & 0xFF) << 16) | + ((u32)(data[3] & 0xFF) << 8) | + (u32)(data[4] & 0xFF); /* Set SLV-T Bank : 0xA0 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); @@ -1177,40 +1471,30 @@ static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv) if (period == 0) { dev_dbg(&priv->i2c->dev, "%s(): period is 0\n", __func__); - return 0; + return -EINVAL; } - if (bit_error > (period * 64800)) { + if (*bit_error > (period * 64800)) { dev_dbg(&priv->i2c->dev, "%s(): invalid bit_err 0x%x period 0x%x\n", - __func__, bit_error, period); - return 0; + __func__, *bit_error, period); + return -EINVAL; } - /* - * BER = bitError / (period * 64800) - * = (bitError * 10^7) / (period * 64800) - * = (bitError * 10^5) / (period * 648) - * = (bitError * 12500) / (period * 81) - * = (bitError * 10) * 1250 / (period * 81) - */ - temp_q = div_u64_rem(12500ULL * bit_error, - period * 81, &temp_r); - if (temp_r >= period * 40) - temp_q++; - result = temp_q; + *bit_count = period * 64800; + + return 0; } else { dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); } - return result; + return -EINVAL; } -static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) +static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) { u8 data[4]; - u32 div, q, r; - u32 bit_err, period_exp, n_ldpc; + u32 period_exp, n_ldpc; - *ber = 0; if (priv->state != STATE_ACTIVE_TC) { dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", __func__, priv->state); @@ -1221,40 +1505,44 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) if (!(data[0] & 0x10)) { dev_dbg(&priv->i2c->dev, "%s(): no valid BER data\n", __func__); - return 0; + return -EINVAL; } - bit_err = ((u32)(data[0] & 0x0f) << 24) | - ((u32)data[1] << 16) | - ((u32)data[2] << 8) | - (u32)data[3]; + *bit_error = ((u32)(data[0] & 0x0f) << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + (u32)data[3]; cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); period_exp = data[0] & 0x0f; cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22); cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data); n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800); - if (bit_err > ((1U << period_exp) * n_ldpc)) { + if (*bit_error > ((1U << period_exp) * n_ldpc)) { dev_dbg(&priv->i2c->dev, "%s(): invalid BER value\n", __func__); return -EINVAL; } + + /* + * FIXME: the right thing would be to return bit_error untouched, + * but, as we don't know the scale returned by the counters, let's + * at least preserver BER = bit_error/bit_count. + */ if (period_exp >= 4) { - div = (1U << (period_exp - 4)) * (n_ldpc / 200); - q = div_u64_rem(3125ULL * bit_err, div, &r); + *bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200); + *bit_error *= 3125ULL; } else { - div = (1U << period_exp) * (n_ldpc / 200); - q = div_u64_rem(50000ULL * bit_err, div, &r); + *bit_count = (1U << period_exp) * (n_ldpc / 200); + *bit_error *= 50000ULL; } - *ber = (r >= div / 2) ? q + 1 : q; return 0; } -static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber) +static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) { u8 data[2]; - u32 div, q, r; - u32 bit_err, period; + u32 period; - *ber = 0; if (priv->state != STATE_ACTIVE_TC) { dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", __func__, priv->state); @@ -1268,16 +1556,22 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber) return 0; } cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data)); - bit_err = ((u32)data[0] << 8) | (u32)data[1]; + *bit_error = ((u32)data[0] << 8) | (u32)data[1]; cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07)); - div = period / 128; - q = div_u64_rem(78125ULL * bit_err, div, &r); - *ber = (r >= div / 2) ? q + 1 : q; + + /* + * FIXME: the right thing would be to return bit_error untouched, + * but, as we don't know the scale returned by the counters, let's + * at least preserver BER = bit_error/bit_count. + */ + *bit_count = period / 128; + *bit_error *= 78125ULL; return 0; } -static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys) +static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, + u8 delsys, u32 *snr) { u8 data[3]; u32 res = 0, value; @@ -1335,9 +1629,71 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys) } else { dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); + return -EINVAL; } done: - return res; + *snr = res; + return 0; +} + +static uint32_t sony_log(uint32_t x) +{ + return (((10000>>8)*(intlog2(x)>>16) + LOG2_E_100X/2)/LOG2_E_100X); +} + +static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + enum sony_dvbc_constellation_t qam = SONY_DVBC_CONSTELLATION_16QAM; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + + /* + * Freeze registers: ensure multiple separate register reads + * are from the same snapshot + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1); + qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4C, data, 2); + + reg = ((u32)(data[0]&0x1f) << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + + switch (qam) { + case SONY_DVBC_CONSTELLATION_16QAM: + case SONY_DVBC_CONSTELLATION_64QAM: + case SONY_DVBC_CONSTELLATION_256QAM: + /* SNR(dB) = -9.50 * ln(IREG_SNR_ESTIMATE / (24320)) */ + if (reg < 126) + reg = 126; + *snr = -95 * (int32_t)sony_log(reg) + 95941; + break; + case SONY_DVBC_CONSTELLATION_32QAM: + case SONY_DVBC_CONSTELLATION_128QAM: + /* SNR(dB) = -8.75 * ln(IREG_SNR_ESTIMATE / (20800)) */ + if (reg < 69) + reg = 69; + *snr = -88 * (int32_t)sony_log(reg) + 86999; + break; + default: + return -EINVAL; + } + + return 0; } static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) @@ -1391,14 +1747,80 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) return 0; } -static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, - u8 delsys) +static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, + priv->state); + return -EINVAL; + } + + /* Freeze all registers */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + + + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 100 * intlog10(reg) - 9031; + return 0; +} + +static u16 cxd2841er_read_agc_gain_c(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x49, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, + u8 delsys) { u8 data[2]; cxd2841er_write_reg( priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; } @@ -1417,101 +1839,170 @@ static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv) return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3; } -static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber) +static void cxd2841er_read_ber(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; + u32 ret, bit_error = 0, bit_count = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); - *ber = 0; switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + ret = cxd2841er_read_ber_c(priv, &bit_error, &bit_count); + break; case SYS_DVBS: - *ber = cxd2841er_mon_read_ber_s(priv); + ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count); break; case SYS_DVBS2: - *ber = cxd2841er_mon_read_ber_s2(priv); + ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count); break; case SYS_DVBT: - return cxd2841er_read_ber_t(priv, ber); + ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count); + break; case SYS_DVBT2: - return cxd2841er_read_ber_t2(priv, ber); - default: - *ber = 0; + ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count); break; + default: + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + if (!ret) { + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue += bit_error; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].uvalue += bit_count; + } else { + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - return 0; } -static int cxd2841er_read_signal_strength(struct dvb_frontend *fe, - u16 *strength) +static void cxd2841er_read_signal_strength(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; + s32 strength; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { case SYS_DVBT: case SYS_DVBT2: - *strength = 65535 - cxd2841er_read_agc_gain_t_t2( - priv, p->delivery_system); + strength = cxd2841er_read_agc_gain_t_t2(priv, + p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* Formula was empirically determinated @ 410 MHz */ + p->strength.stat[0].uvalue = strength * 366 / 100 - 89520; + break; /* Code moved out of the function */ + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + strength = cxd2841er_read_agc_gain_c(priv, + p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* + * Formula was empirically determinated via linear regression, + * using frequencies: 175 MHz, 410 MHz and 800 MHz, and a + * stream modulated with QAM64 + */ + p->strength.stat[0].uvalue = strength * 4045 / 1000 - 85224; + break; + case SYS_ISDBT: + strength = cxd2841er_read_agc_gain_i(priv, p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* + * Formula was empirically determinated via linear regression, + * using frequencies: 175 MHz, 410 MHz and 800 MHz. + */ + p->strength.stat[0].uvalue = strength * 3775 / 1000 - 90185; break; case SYS_DVBS: case SYS_DVBS2: - *strength = 65535 - cxd2841er_read_agc_gain_s(priv); + strength = 65535 - cxd2841er_read_agc_gain_s(priv); + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->strength.stat[0].uvalue = strength; break; default: - *strength = 0; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; break; } - return 0; } -static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr) +static void cxd2841er_read_snr(struct dvb_frontend *fe) { u32 tmp = 0; + int ret = 0; struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + ret = cxd2841er_read_snr_c(priv, &tmp); + break; case SYS_DVBT: - cxd2841er_read_snr_t(priv, &tmp); + ret = cxd2841er_read_snr_t(priv, &tmp); break; case SYS_DVBT2: - cxd2841er_read_snr_t2(priv, &tmp); + ret = cxd2841er_read_snr_t2(priv, &tmp); + break; + case SYS_ISDBT: + ret = cxd2841er_read_snr_i(priv, &tmp); break; case SYS_DVBS: case SYS_DVBS2: - tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system); + ret = cxd2841er_dvbs_read_snr(priv, p->delivery_system, &tmp); break; default: dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n", __func__, p->delivery_system); - break; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + if (!ret) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = tmp; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - *snr = tmp & 0xffff; - return 0; } -static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; + u32 ucblocks; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + cxd2841er_read_packet_errors_c(priv, &ucblocks); + break; case SYS_DVBT: - cxd2841er_read_packet_errors_t(priv, ucblocks); + cxd2841er_read_packet_errors_t(priv, &ucblocks); break; case SYS_DVBT2: - cxd2841er_read_packet_errors_t2(priv, ucblocks); + cxd2841er_read_packet_errors_t2(priv, &ucblocks); break; - default: - *ucblocks = 0; + case SYS_ISDBT: + cxd2841er_read_packet_errors_i(priv, &ucblocks); break; + default: + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; } dev_dbg(&priv->i2c->dev, "%s()\n", __func__); - return 0; + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = ucblocks; } static int cxd2841er_dvbt2_set_profile( @@ -1524,15 +2015,18 @@ static int cxd2841er_dvbt2_set_profile( switch (profile) { case DVBT2_PROFILE_BASE: tune_mode = 0x01; - seq_not2d_time = 12; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C; break; case DVBT2_PROFILE_LITE: tune_mode = 0x05; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; case DVBT2_PROFILE_ANY: tune_mode = 0x00; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; default: return -EINVAL; @@ -1574,254 +2068,617 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, u32 bandwidth) { u32 iffreq; - u8 b20_9f[5]; - u8 b10_a6[14]; - u8 b10_b6[3]; - u8 b10_d7; + u8 data[MAX_WRITE_REGSIZE]; + + const uint8_t nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + + const uint8_t nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; + + const uint8_t nominalRate17bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */ + {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC} /* 41MHz XTal */ + }; + + const uint8_t itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, + 0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + + const uint8_t itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, + 0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + + const uint8_t itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef17bw[3][14] = { + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */ + {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B, + 0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal */ + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (bandwidth) { case 8000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x11; - b20_9f[1] = 0xf0; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x26; - b10_a6[1] = 0xaf; - b10_a6[2] = 0x06; - b10_a6[3] = 0xcd; - b10_a6[4] = 0x13; - b10_a6[5] = 0xbb; - b10_a6[6] = 0x28; - b10_a6[7] = 0xba; - b10_a6[8] = 0x23; - b10_a6[9] = 0xa9; - b10_a6[10] = 0x1f; - b10_a6[11] = 0xa8; - b10_a6[12] = 0x2c; - b10_a6[13] = 0xc8; - iffreq = MAKE_IFFREQ_CONFIG(4.80); - b10_d7 = 0x00; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); break; case 7000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x14; - b20_9f[1] = 0x80; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x2C; - b10_a6[1] = 0xBD; - b10_a6[2] = 0x02; - b10_a6[3] = 0xCF; - b10_a6[4] = 0x04; - b10_a6[5] = 0xF8; - b10_a6[6] = 0x23; - b10_a6[7] = 0xA6; - b10_a6[8] = 0x29; - b10_a6[9] = 0xB0; - b10_a6[10] = 0x26; - b10_a6[11] = 0xA9; - b10_a6[12] = 0x21; - b10_a6[13] = 0xA5; - iffreq = MAKE_IFFREQ_CONFIG(4.2); - b10_d7 = 0x02; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); break; case 6000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x17; - b20_9f[1] = 0xEA; - b20_9f[2] = 0xAA; - b20_9f[3] = 0xAA; - b20_9f[4] = 0xAA; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x04; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); break; case 5000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x1C; - b20_9f[1] = 0xB3; - b20_9f[2] = 0x33; - b20_9f[3] = 0x33; - b20_9f[4] = 0x33; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x06; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); break; case 1712000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x58; - b20_9f[1] = 0xE2; - b20_9f[2] = 0xAF; - b20_9f[3] = 0xE0; - b20_9f[4] = 0xBC; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x25; - b10_a6[1] = 0xA0; - b10_a6[2] = 0x36; - b10_a6[3] = 0x8D; - b10_a6[4] = 0x2E; - b10_a6[5] = 0x94; - b10_a6[6] = 0x28; - b10_a6[7] = 0x9B; - b10_a6[8] = 0x32; - b10_a6[9] = 0x90; - b10_a6[10] = 0x2C; - b10_a6[11] = 0x9D; - b10_a6[12] = 0x29; - b10_a6[13] = 0x99; - iffreq = MAKE_IFFREQ_CONFIG(3.5); - b10_d7 = 0x03; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate17bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x03, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef17bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.50); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x03, 0x07); break; default: return -EINVAL; } - /* Set SLV-T Bank : 0x20 */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20); - cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f)); - /* Set SLV-T Bank : 0x27 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); - cxd2841er_set_reg_bits( - priv, I2C_SLVT, 0x7a, - (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f); - /* Set SLV-T Bank : 0x10 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); - /* Group delay equaliser sett. for ASCOT2E */ - cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6)); - /* */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - /* System bandwidth setting */ - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07); return 0; } static int cxd2841er_sleep_tc_to_active_t_band( struct cxd2841er_priv *priv, u32 bandwidth) { - u8 b13_9c[2] = { 0x01, 0x14 }; - u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 }; - u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, - 0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 }; - u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 }; - u8 bw8mhz_b17_38[] = { 0x01, 0x02 }; - u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 }; - u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, - 0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 }; - u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 }; - u8 bw7mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA }; - u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC }; - u8 bw6mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 }; - u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw5mhz_b10_d9[] = { 0x26, 0x3C }; - u8 bw5mhz_b17_38[] = { 0x00, 0x03 }; - u8 b10_b6[3]; - u8 d7val; + u8 data[MAX_WRITE_REGSIZE]; u32 iffreq; - u8 *b10_9f; - u8 *b10_a6; - u8 *b10_d9; - u8 *b17_38; + u8 nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + u8 nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + u8 itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5, + 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + u8 itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2, + 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + u8 itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x13 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); /* Echo performance optimization setting */ - cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c)); + data[0] = 0x01; + data[1] = 0x14; + cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2); + + /* Set SLV-T Bank : 0x10 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: - b10_9f = bw8mhz_b10_9f; - b10_a6 = bw8mhz_b10_a6; - b10_d9 = bw8mhz_b10_d9; - b17_38 = bw8mhz_b17_38; - d7val = 0; - iffreq = MAKE_IFFREQ_CONFIG(4.80); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x15; + data[1] = 0x28; + } else { + data[0] = 0x01; + data[1] = 0xE0; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x01; + data[1] = 0x02; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 7000000: - b10_9f = bw7mhz_b10_9f; - b10_a6 = bw7mhz_b10_a6; - b10_d9 = bw7mhz_b10_d9; - b17_38 = bw7mhz_b17_38; - d7val = 2; - iffreq = MAKE_IFFREQ_CONFIG(4.20); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0xF8; + } else { + data[0] = 0x12; + data[1] = 0xF8; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 6000000: - b10_9f = bw6mhz_b10_9f; - b10_a6 = bw6mhz_b10_a6; - b10_d9 = bw6mhz_b10_d9; - b17_38 = bw6mhz_b17_38; - d7val = 4; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x25; + data[1] = 0x4C; + } else { + data[0] = 0x1F; + data[1] = 0xDC; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 5000000: - b10_9f = bw5mhz_b10_9f; - b10_a6 = bw5mhz_b10_a6; - b10_d9 = bw5mhz_b10_d9; - b17_38 = bw5mhz_b17_38; - d7val = 6; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x2C; + data[1] = 0xC2; + } else { + data[0] = 0x26; + data[1] = 0x3C; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + } + + return 0; +} + +static int cxd2841er_sleep_tc_to_active_i_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u32 iffreq; + u8 data[3]; + + /* TRCG Nominal Rate */ + u8 nominalRate8bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate7bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate6bw[3][5] = { + {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x2E, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 itbCoef8bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, + 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */ + {0x0}, /* 41MHz XTal */ + }; + + u8 itbCoef7bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, + 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */ + {0x00}, /* 41MHz XTal */ + }; + + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, + 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal */ + }; + + dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* 20.5/41MHz Xtal support is not available + * on ISDB-T 7MHzBW and 8MHzBW + */ + if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) { + dev_err(&priv->i2c->dev, + "%s(): bandwidth %d supported only for 24MHz xtal\n", + __func__, bandwidth); + return -EINVAL; + } + + switch (bandwidth) { + case 8000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.75); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7); + + /* Demod core latency setting */ + data[0] = 0x13; + data[1] = 0xFC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03); + break; + case 7000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.15); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7); + + /* Demod core latency setting */ + data[0] = 0x1A; + data[1] = 0xFA; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); + break; + case 6000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.55); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0x79; + } else { + data[0] = 0x1A; + data[1] = 0xE2; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); break; default: dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", - __func__, bandwidth); + __func__, bandwidth); return -EINVAL; } - /* */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f)); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6)); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9)); - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38)); return 0; } @@ -1837,7 +2694,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u8 b10_b6[3]; u32 iffreq; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: @@ -1854,7 +2711,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, iffreq = MAKE_IFFREQ_CONFIG(3.7); break; default: - dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", __func__, bandwidth); return -EINVAL; } @@ -1902,6 +2759,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, u32 bandwidth) { u8 data[2] = { 0x09, 0x54 }; + u8 data24m[3] = {0xDC, 0x6C, 0x00}; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); @@ -1919,7 +2777,11 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + /* Enable ADC 2 & 3 */ + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -1947,6 +2809,15 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, /* TSIF setting */ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3); + } + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); @@ -1961,7 +2832,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, u32 bandwidth) { - u8 data[2] = { 0x09, 0x54 }; + u8 data[MAX_WRITE_REGSIZE]; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); @@ -1974,12 +2845,21 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Enable demod clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); /* Enable ADC clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } else { + data[0] = 0x09; + data[1] = 0x54; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -2002,6 +2882,10 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Set SLV-T Bank : 0x2b */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + /* L1 Control setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* TSIF setting */ @@ -2020,6 +2904,72 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + /* 24MHz Xtal setting */ + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + data[0] = 0xEB; + data[1] = 0x03; + data[2] = 0x3B; + cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3); + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + data[0] = 0x5E; + data[1] = 0x5E; + data[2] = 0x47; + cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18); + + data[0] = 0x3F; + data[1] = 0xFF; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Set SLV-T Bank : 0x24 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + data[0] = 0x0B; + data[1] = 0x72; + cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2); + + data[0] = 0x93; + data[1] = 0xF3; + data[2] = 0x00; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3); + + data[0] = 0x05; + data[1] = 0xB8; + data[2] = 0xD8; + cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00); + + /* Set SLV-T Bank : 0x25 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25); + cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34); + + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B); + cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E); + + /* Set SLV-T Bank : 0x2D */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D); + data[0] = 0x89; + data[1] = 0x89; + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2); + + /* Set SLV-T Bank : 0x5E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E); + data[0] = 0x24; + data[1] = 0x95; + cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2); + } + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ @@ -2032,6 +2982,84 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, return 0; } +/* ISDB-Tb part */ +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + u8 data24m[2] = {0x60, 0x00}; + u8 data24m2[3] = {0xB7, 0x1B, 0x00}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz or 24M */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01); + /* ISDB-T initial setting */ + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F); + /* Set SLV-T Bank : 0x15 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03); + /* Set SLV-T Bank : 0x1E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF); + /* Set SLV-T Bank : 0x63 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01); + + /* for xtal 24MHz */ + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2); + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3); + + cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, u32 bandwidth) { @@ -2079,7 +3107,7 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); - cxd2841er_sleep_tc_to_active_c_band(priv, 8000000); + cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* Disable HiZ Setting 1 */ @@ -2094,8 +3122,6 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *p) { enum fe_status status = 0; - u16 strength = 0, snr = 0; - u32 errors = 0, ber = 0; struct cxd2841er_priv *priv = fe->demodulator_priv; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); @@ -2104,32 +3130,18 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe, else if (priv->state == STATE_ACTIVE_TC) cxd2841er_read_status_tc(fe, &status); + cxd2841er_read_signal_strength(fe); + if (status & FE_HAS_LOCK) { - cxd2841er_read_signal_strength(fe, &strength); - p->strength.len = 1; - p->strength.stat[0].scale = FE_SCALE_RELATIVE; - p->strength.stat[0].uvalue = strength; - cxd2841er_read_snr(fe, &snr); - p->cnr.len = 1; - p->cnr.stat[0].scale = FE_SCALE_DECIBEL; - p->cnr.stat[0].svalue = snr; - cxd2841er_read_ucblocks(fe, &errors); - p->block_error.len = 1; - p->block_error.stat[0].scale = FE_SCALE_COUNTER; - p->block_error.stat[0].uvalue = errors; - cxd2841er_read_ber(fe, &ber); - p->post_bit_error.len = 1; - p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - p->post_bit_error.stat[0].uvalue = ber; + cxd2841er_read_snr(fe); + cxd2841er_read_ucblocks(fe); + + cxd2841er_read_ber(fe); } else { - p->strength.len = 1; - p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - p->cnr.len = 1; p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - p->block_error.len = 1; p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - p->post_bit_error.len = 1; p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } return 0; } @@ -2142,10 +3154,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; u32 symbol_rate = p->symbol_rate/1000; - dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n", + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n", __func__, (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), - p->frequency, symbol_rate); + p->frequency, symbol_rate, priv->xtal); switch (priv->state) { case STATE_SLEEP_S: ret = cxd2841er_sleep_s_to_active_s( @@ -2189,6 +3201,13 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) __func__, carr_offset); } done: + /* Reset stats */ + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return ret; } @@ -2199,7 +3218,8 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n", + __func__, p->delivery_system, p->bandwidth_hz); if (p->delivery_system == SYS_DVBT) { priv->system = SYS_DVBT; switch (priv->state) { @@ -2233,9 +3253,33 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) __func__, priv->state); ret = -EINVAL; } + } else if (p->delivery_system == SYS_ISDBT) { + priv->system = SYS_ISDBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } } else if (p->delivery_system == SYS_DVBC_ANNEX_A || p->delivery_system == SYS_DVBC_ANNEX_C) { priv->system = SYS_DVBC_ANNEX_A; + /* correct bandwidth */ + if (p->bandwidth_hz != 6000000 && + p->bandwidth_hz != 7000000 && + p->bandwidth_hz != 8000000) { + p->bandwidth_hz = 8000000; + dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n", + __func__, p->bandwidth_hz); + } + switch (priv->state) { case STATE_SLEEP_TC: ret = cxd2841er_sleep_tc_to_active_c( @@ -2321,7 +3365,8 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune); + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__, + re_tune, p->bandwidth_hz); if (re_tune) { ret = cxd2841er_set_frontend_tc(fe); if (ret) @@ -2329,7 +3374,16 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, cxd2841er_read_status_tc(fe, status); if (*status & FE_HAS_LOCK) { switch (priv->system) { + case SYS_ISDBT: + ret = cxd2841er_get_carrier_offset_i( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT: + ret = cxd2841er_get_carrier_offset_t( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT2: ret = cxd2841er_get_carrier_offset_t2( priv, p->bandwidth_hz, @@ -2382,6 +3436,9 @@ static int cxd2841er_sleep_tc(struct dvb_frontend *fe) case SYS_DVBT2: cxd2841er_active_t2_to_sleep_tc(priv); break; + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + break; case SYS_DVBC_ANNEX_A: cxd2841er_active_c_to_sleep_tc(priv); break; @@ -2512,23 +3569,57 @@ static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe) return DVBFE_ALGO_HW; } +static void cxd2841er_init_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.len = 1; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; +} + + static int cxd2841er_init_s(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + /* sanity. force demod to SHUTDOWN state */ + if (priv->state == STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n", + __func__); + cxd2841er_sleep_s_to_shutdown(priv); + } else if (priv->state == STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n", + __func__); + cxd2841er_active_s_to_sleep_s(priv); + cxd2841er_sleep_s_to_shutdown(priv); + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_shutdown_to_sleep_s(priv); /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01); + + cxd2841er_init_stats(fe); + return 0; } static int cxd2841er_init_tc(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n", + __func__, p->bandwidth_hz); cxd2841er_shutdown_to_sleep_tc(priv); /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); @@ -2538,12 +3629,14 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe) /* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80); + + cxd2841er_init_stats(fe); + return 0; } static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; -static struct dvb_frontend_ops cxd2841er_dvbc_ops; +static struct dvb_frontend_ops cxd2841er_t_c_ops; static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, struct i2c_adapter *i2c, @@ -2551,6 +3644,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, { u8 chip_id = 0; const char *type; + const char *name; struct cxd2841er_priv *priv = NULL; /* allocate memory for the internal state */ @@ -2561,46 +3655,49 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, priv->config = cfg; priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; - /* create dvb_frontend */ - switch (system) { - case SYS_DVBS: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbs_s2_ops, - sizeof(struct dvb_frontend_ops)); - type = "S/S2"; - break; - case SYS_DVBT: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbt_t2_ops, - sizeof(struct dvb_frontend_ops)); - type = "T/T2"; - break; - case SYS_DVBC_ANNEX_A: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbc_ops, - sizeof(struct dvb_frontend_ops)); - type = "C/C2"; - break; - default: - kfree(priv); - return NULL; - } + priv->xtal = cfg->xtal; priv->frontend.demodulator_priv = priv; - dev_info(&priv->i2c->dev, - "%s(): attaching CXD2841ER DVB-%s frontend\n", - __func__, type); dev_info(&priv->i2c->dev, "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", __func__, priv->i2c, priv->i2c_addr_slvx, priv->i2c_addr_slvt); chip_id = cxd2841er_chip_id(priv); - if (chip_id != CXD2841ER_CHIP_ID) { + switch (chip_id) { + case CXD2841ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2841ER DVB-T/T2/C demodulator"); + name = "CXD2841ER"; + break; + case CXD2854ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator"); + cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT; + name = "CXD2854ER"; + break; + default: dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", - __func__, chip_id); + __func__, chip_id); priv->frontend.demodulator_priv = NULL; kfree(priv); return NULL; } + + /* create dvb_frontend */ + if (system == SYS_DVBS) { + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + } else { + memcpy(&priv->frontend.ops, + &cxd2841er_t_c_ops, + sizeof(struct dvb_frontend_ops)); + type = "T/T2/C/ISDB-T"; + } + + dev_info(&priv->i2c->dev, + "%s(): attaching %s DVB-%s frontend\n", + __func__, name, type); dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", __func__, chip_id); return &priv->frontend; @@ -2613,19 +3710,12 @@ struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, } EXPORT_SYMBOL(cxd2841er_attach_s); -struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c) -{ - return cxd2841er_attach(cfg, i2c, SYS_DVBT); -} -EXPORT_SYMBOL(cxd2841er_attach_t); - -struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, +struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A); + return cxd2841er_attach(cfg, i2c, 0); } -EXPORT_SYMBOL(cxd2841er_attach_c); +EXPORT_SYMBOL(cxd2841er_attach_t_c); static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, @@ -2655,10 +3745,10 @@ static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .tune = cxd2841er_tune_s }; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { - .delsys = { SYS_DVBT, SYS_DVBT2 }, +static struct dvb_frontend_ops cxd2841er_t_c_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, .info = { - .name = "Sony CXD2841ER DVB-T/T2 demodulator", + .name = "", /* will set in attach function */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | @@ -2691,37 +3781,6 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { .get_frontend_algo = cxd2841er_get_algo }; -static struct dvb_frontend_ops cxd2841er_dvbc_ops = { - .delsys = { SYS_DVBC_ANNEX_A }, - .info = { - .name = "Sony CXD2841ER DVB-C demodulator", - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO | - FE_CAN_INVERSION_AUTO, - .frequency_min = 42000000, - .frequency_max = 1002000000 - }, - .init = cxd2841er_init_tc, - .sleep = cxd2841er_sleep_tc, - .release = cxd2841er_release, - .set_frontend = cxd2841er_set_frontend_tc, - .get_frontend = cxd2841er_get_frontend, - .read_status = cxd2841er_read_status_tc, - .tune = cxd2841er_tune_tc, - .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, - .get_frontend_algo = cxd2841er_get_algo, -}; - -MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver"); -MODULE_AUTHOR("Sergey Kozlov "); +MODULE_DESCRIPTION("Sony CXD2841ER/CXD2854ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov , Abylay Ospan "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h index 3472bdd58949..62ad5f07390b 100644 --- a/drivers/media/dvb-frontends/cxd2841er.h +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -25,41 +25,39 @@ #include #include +enum cxd2841er_xtal { + SONY_XTAL_20500, /* 20.5 MHz */ + SONY_XTAL_24000, /* 24 MHz */ + SONY_XTAL_41000 /* 41 MHz */ +}; + struct cxd2841er_config { u8 i2c_addr; + enum cxd2841er_xtal xtal; }; #if IS_REACHABLE(CONFIG_DVB_CXD2841ER) extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); -extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c); - -extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, +extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); #else static inline struct dvb_frontend *cxd2841er_attach_s( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline struct dvb_frontend *cxd2841er_attach_t( +static inline struct dvb_frontend *cxd2841er_attach_t_c( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline struct dvb_frontend *cxd2841er_attach_c( - struct cxd2841er_config *cfg, struct i2c_adapter *i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} #endif #endif diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h index 33e2f495277b..0bbce451149f 100644 --- a/drivers/media/dvb-frontends/cxd2841er_priv.h +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -26,6 +26,7 @@ #define I2C_SLVT 1 #define CXD2841ER_CHIP_ID 0xa7 +#define CXD2854ER_CHIP_ID 0xc1 #define CXD2841ER_DVBS_POLLING_INVL 10 diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index d879dc0607f4..14c403254fe0 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -797,6 +797,8 @@ static const u16 bb_ramp_pwm_normal[] = { (0 << 9) | 400, /* BB_RAMP6 */ }; +#if 0 +/* Currently unused */ static const u16 bb_ramp_pwm_boost[] = { 550, /* max BB gain in 10th of dB */ 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ @@ -806,6 +808,7 @@ static const u16 bb_ramp_pwm_boost[] = { (2 << 9) | 208, /* BB_RAMP5 = 29dB */ (0 << 9) | 440, /* BB_RAMP6 */ }; +#endif static const u16 rf_ramp_pwm_cband[] = { 314, /* max RF gain in 10th of dB */ @@ -849,6 +852,8 @@ static const u16 rf_ramp_pwm_uhf[] = { (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ }; +#if 0 +/* Currently unused */ static const u16 rf_ramp_pwm_sband[] = { 253, /* max RF gain in 10th of dB */ 38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ @@ -862,6 +867,7 @@ static const u16 rf_ramp_pwm_sband[] = { (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ }; +#endif struct slope { s16 range; diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index e48b741d439e..bd6d2ee0f7c9 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -1240,12 +1240,15 @@ static u32 frac_times1e6(u32 N, u32 D) * and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20). * */ +#if 0 +/* Currently, unused as we lack support for analog TV */ static const u16 nicam_presc_table_val[43] = { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 23, 25, 28, 32, 36, 40, 45, 51, 57, 64, 71, 80, 90, 101, 113, 127 }; +#endif /*============================================================================*/ /*== END HELPER FUNCTIONS ==*/ diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index addffc33993a..447b518e287a 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -959,6 +959,15 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* enable ac coupling */ ds3000_writereg(state, 0x25, 0x8a); + if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) || + (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) { + dprintk("%s() symbol_rate %u out of range (%u ... %u)\n", + __func__, c->symbol_rate, + ds3000_ops.info.symbol_rate_min, + ds3000_ops.info.symbol_rate_max); + return -EINVAL; + } + /* enhance symbol rate performance */ if ((c->symbol_rate / 1000) <= 5000) { value = 29777 / (c->symbol_rate / 1000) + 1; diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c new file mode 100644 index 000000000000..97a8982740a6 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.c @@ -0,0 +1,1042 @@ +/* + * helene.c + * + * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "helene.h" +#include "dvb_frontend.h" + +#define MAX_WRITE_REGSIZE 20 + +enum helene_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct helene_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum helene_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); + enum helene_xtal xtal; +}; + +#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \ + (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC_8)\ + && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC2_8)) + +#define HELENE_AUTO 0xff +#define HELENE_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define HELENE_BW_6 0x00 +#define HELENE_BW_7 0x01 +#define HELENE_BW_8 0x02 +#define HELENE_BW_1_7 0x03 + +enum helene_tv_system_t { + SONY_HELENE_TV_SYSTEM_UNKNOWN, + /* Terrestrial Analog */ + SONY_HELENE_ATV_MN_EIAJ, + /**< System-M (Japan) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_SAP, + /**< System-M (US) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_A2, + /**< System-M (Korea) (IF: Fp=5.9MHz in default) */ + SONY_HELENE_ATV_BG, + /**< System-B/G (IF: Fp=7.3MHz in default) */ + SONY_HELENE_ATV_I, + /**< System-I (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_DK, + /**< System-D/K (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L, + /**< System-L (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L_DASH, + /**< System-L DASH (IF: Fp=2.2MHz in default) */ + /* Terrestrial/Cable Digital */ + SONY_HELENE_DTV_8VSB, + /**< ATSC 8VSB (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_QAM, + /**< US QAM (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_ISDBT_6, + /**< ISDB-T 6MHzBW (IF: Fc=3.55MHz in default) */ + SONY_HELENE_DTV_ISDBT_7, + /**< ISDB-T 7MHzBW (IF: Fc=4.15MHz in default) */ + SONY_HELENE_DTV_ISDBT_8, + /**< ISDB-T 8MHzBW (IF: Fc=4.75MHz in default) */ + SONY_HELENE_DTV_DVBT_5, + /**< DVB-T 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_6, + /**< DVB-T 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_7, + /**< DVB-T 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT_8, + /**< DVB-T 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBT2_1_7, + /**< DVB-T2 1.7MHzBW (IF: Fc=3.5MHz in default) */ + SONY_HELENE_DTV_DVBT2_5, + /**< DVB-T2 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_6, + /**< DVB-T2 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_7, + /**< DVB-T2 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT2_8, + /**< DVB-T2 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBC_6, + /**< DVB-C 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC_8, + /**< DVB-C 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DVBC2_6, + /**< DVB-C2 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC2_8, + /**< DVB-C2 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DTMB, + /**< DTMB (IF: Fc=5.1MHz in default) */ + /* Satellite */ + SONY_HELENE_STV_ISDBS, + /**< ISDB-S */ + SONY_HELENE_STV_DVBS, + /**< DVB-S */ + SONY_HELENE_STV_DVBS2, + /**< DVB-S2 */ + + SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ, + /**< Minimum analog terrestrial system */ + SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH, + /**< Maximum analog terrestrial system */ + SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB, + /**< Minimum digital terrestrial system */ + SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB, + /**< Maximum digital terrestrial system */ + SONY_HELENE_TERR_TV_SYSTEM_NUM, + /**< Number of supported terrestrial broadcasting system */ + SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS, + /**< Minimum satellite system */ + SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2 + /**< Maximum satellite system */ +}; + +struct helene_terr_adjust_param_t { + /* < Addr:0x69 Bit[6:4] : RFVGA gain. + * 0xFF means Auto. (RF_GAIN_SEL = 1) + */ + uint8_t RF_GAIN; + /* < Addr:0x69 Bit[3:0] : IF_BPF gain. + */ + uint8_t IF_BPF_GC; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF <= 172MHz) + */ + uint8_t RFOVLD_DET_LV1_VL; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t RFOVLD_DET_LV1_VH; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF > 464MHz) + */ + uint8_t RFOVLD_DET_LV1_U; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF <= 172MHz) + */ + uint8_t IFOVLD_DET_LV_VL; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t IFOVLD_DET_LV_VH; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF > 464MHz) + */ + uint8_t IFOVLD_DET_LV_U; + /* < Addr:0x6D Bit[5:4] : + * IF filter center offset. + */ + uint8_t IF_BPF_F0; + /* < Addr:0x6D Bit[1:0] : + * 6MHzBW(0x00) or 7MHzBW(0x01) + * or 8MHzBW(0x02) or 1.7MHzBW(0x03) + */ + uint8_t BW; + /* < Addr:0x6E Bit[4:0] : + * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50 + */ + uint8_t FIF_OFFSET; + /* < Addr:0x6F Bit[4:0] : + * 5bit signed. BW offset (kHz) = + * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW) + */ + uint8_t BW_OFFSET; + /* < Addr:0x9C Bit[0] : + * Local polarity. (0: Upper Local, 1: Lower Local) + */ + uint8_t IS_LOWERLOCAL; +}; + +static const struct helene_terr_adjust_param_t +terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = { + /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */ + {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(0), 0x00}, + /* Analog */ + /**< SONY_HELENE_ATV_MN_EIAJ (System-M (Japan)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_SAP (System-M (US)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(3), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_A2 (System-M (Korea)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_7, HELENE_OFFSET(11), HELENE_OFFSET(5), 0x00}, + /**< SONY_HELENE_ATV_BG (System-B/G) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_I (System-I) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_DK (System-D/K) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_L (System-L) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(-1), HELENE_OFFSET(4), 0x00}, + /**< SONY_HELENE_ATV_L_DASH (System-L DASH) */ + /* Digital */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_8VSB (ATSC 8VSB) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_QAM (US QAM) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_6 (ISDB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_7 (ISDB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_8 (ISDB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_5 (DVB-T 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_6 (DVB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT_7 (DVB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT_8 (DVB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_5 (DVB-T2 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_6 (DVB-T2 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_7 (DVB-T2 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_8 (DVB-T2 8MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00}, + /**< SONY_HELENE_DTV_DVBC_6 (DVB-C 6MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBC_8 (DVB-C 8MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_6 (DVB-C2 6MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(0), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_8 (DVB-C2 8MHzBW) */ + {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(1), 0x00} + /**< SONY_HELENE_DTV_DTMB (DTMB) */ +}; + +static void helene_i2c_debug(struct helene_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("helene: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int helene_write_regs(struct helene_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev, + "wr reg=%04x: len=%d vs %Zu is too big!\n", + reg, len + 1, sizeof(buf)); + return -E2BIG; + } + + helene_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val) +{ + return helene_write_regs(priv, reg, &val, 1); +} + +static int helene_read_regs(struct helene_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + helene_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val) +{ + return helene_read_regs(priv, reg, val, 1); +} + +static int helene_set_reg_bits(struct helene_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = helene_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return helene_write_reg(priv, reg, data); +} + +static int helene_enter_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x0); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + priv->state = STATE_SLEEP; + return 0; +} + +static int helene_leave_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC4); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x40); + + priv->state = STATE_ACTIVE; + return 0; +} + +static int helene_init(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return helene_leave_power_save(priv); +} + +static int helene_release(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int helene_sleep(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + helene_enter_power_save(priv); + return 0; +} + +static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe) +{ + enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT_8; + else { + system = SONY_HELENE_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT2_8; + else { + system = SONY_HELENE_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBS) { + system = SONY_HELENE_STV_DVBS; + } else if (p->delivery_system == SYS_DVBS2) { + system = SONY_HELENE_STV_DVBS2; + } else if (p->delivery_system == SYS_ISDBS) { + system = SONY_HELENE_STV_ISDBS; + } else if (p->delivery_system == SYS_ISDBT) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_ISDBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_ISDBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_ISDBT_8; + else { + system = SONY_HELENE_DTV_ISDBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, + p->bandwidth_hz); + return system; +} + +static int helene_set_params_s(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency; + uint32_t frequency4kHz = 0; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n", + __func__, frequencykHz, symbol_rate); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + /* RF switch turn to satellite */ + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + frequency = roundup(p->frequency / 1000, 1); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x15, 0x02); + + /* RFIN matching in power save (Sat) reset */ + helene_write_reg(priv, 0x43, 0x06); + + /* Analog block setting (0x6A, 0x6B) */ + data[0] = 0x00; + data[1] = 0x00; + helene_write_regs(priv, 0x6A, data, 2); + helene_write_reg(priv, 0x75, 0x99); + helene_write_reg(priv, 0x9D, 0x00); + + /* Tuning setting for CPU (0x61) */ + helene_write_reg(priv, 0x61, 0x07); + + /* Satellite mode select (0x01) */ + helene_write_reg(priv, 0x01, 0x01); + + /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */ + data[0] = 0xC4; + data[1] = 0x40; + + switch (priv->xtal) { + case SONY_HELENE_XTAL_16000: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_20500: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_24000: + data[2] = 0x03; + break; + case SONY_HELENE_XTAL_41000: + data[2] = 0x05; + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + + /* Setting for analog block (0x07). LOOPFILTER INTERNAL */ + data[3] = 0x80; + + /* Tuning setting for analog block + * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL + */ + if (priv->xtal == SONY_HELENE_XTAL_20500) + data[4] = 0x58; + else + data[4] = 0x70; + + data[5] = 0x1E; + data[6] = 0x02; + data[7] = 0x24; + + /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */ + data[8] = 0x0F; + data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */ + data[9] = 0x02; + data[10] = 0x1E; + + /* Setting for LPF cutoff frequency (0x0F) */ + switch (tv_system) { + case SONY_HELENE_STV_ISDBS: + data[11] = 0x22; /* 22MHz */ + break; + case SONY_HELENE_STV_DVBS: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 47 + + (40000-1)) / 40000); + else + data[11] = (uint8_t)((symbol_rate * 27 + + (40000-1)) / 40000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + case SONY_HELENE_STV_DVBS2: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 11 + + (10000-1)) / 10000); + else + data[11] = (uint8_t)((symbol_rate * 3 + + (5000-1)) / 5000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n", + __func__, tv_system); + return -EINVAL; + } + + /* RF tuning frequency setting (0x10, 0x11, 0x12) */ + frequency4kHz = (frequencykHz + 2) / 4; + data[12] = (uint8_t)(frequency4kHz & 0xFF); /* FRF_L */ + data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF); /* FRF_M */ + /* FRF_H (bit[3:0]) */ + data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F); + + /* Tuning command (0x13) */ + data[15] = 0xFF; + + /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */ + data[16] = 0x00; + + /* Enable IQ output (0x15) */ + data[17] = 0x01; + + helene_write_regs(priv, 0x04, data, 18); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_set_params(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency / 1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, frequencykHz); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + if (priv->state == STATE_SLEEP) + helene_leave_power_save(priv); + + /* Initial setting for internal analog block (0x91, 0x92) */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[0] = 0x16; + data[1] = 0x26; + } else { + data[0] = 0x10; + data[1] = 0x20; + } + helene_write_regs(priv, 0x91, data, 2); + + /* Setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) + data[0] = 0x90; + else + data[0] = 0x00; + + /* Setting for local polarity (0x9D) */ + data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01); + helene_write_regs(priv, 0x9C, data, 2); + + /* Enable for analog block */ + data[0] = 0xEE; + data[1] = 0x02; + data[2] = 0x1E; + data[3] = 0x67; /* Tuning setting for CPU */ + + /* Setting for PLL reference divider for xtal=24MHz */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) + data[4] = 0x18; + else + data[4] = 0x03; + + /* Tuning setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) { + data[5] = 0x38; + data[6] = 0x1E; + data[7] = 0x02; + data[8] = 0x24; + } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[5] = 0x1C; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x1C; + } else { + data[5] = 0xB4; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x30; + } + helene_write_regs(priv, 0x5E, data, 9); + + /* LT_AMP_EN should be 0 */ + helene_set_reg_bits(priv, 0x67, 0x0, 0x02); + + /* Setting for IFOUT_LIMIT */ + data[0] = 0x00; /* 1.5Vpp */ + + /* RF_GAIN setting */ + if (terr_params[tv_system].RF_GAIN == HELENE_AUTO) + data[1] = 0x80; /* RF_GAIN_SEL = 1 */ + else + data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN + << 4) & 0x70); + + /* IF_BPF_GC setting */ + data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F); + + /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */ + data[2] = 0x00; + if (frequencykHz <= 172000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL + & 0x07); + } else if (frequencykHz <= 464000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH + & 0x07); + } else { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U + & 0x07); + } + data[4] |= 0x20; + + /* Setting for IF frequency and bandwidth */ + + /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */ + data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30); + + /* IF filter band width (BW) (0x6D) */ + data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03); + + /* IF frequency offset value (FIF_OFFSET) (0x6E) */ + data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F); + + /* IF band width offset value (BW_OFFSET) (0x6F) */ + data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F); + + /* RF tuning frequency setting (0x70, 0x71, 0x72) */ + data[8] = (uint8_t)(frequencykHz & 0xFF); /* FRF_L */ + data[9] = (uint8_t)((frequencykHz >> 8) & 0xFF); /* FRF_M */ + data[10] = (uint8_t)((frequencykHz >> 16) + & 0x0F); /* FRF_H (bit[3:0]) */ + + /* Tuning command */ + data[11] = 0xFF; + + /* Enable IF output, AGC and IFOUT pin selection (0x74) */ + data[12] = 0x01; + + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[13] = 0xD9; + data[14] = 0x0F; + data[15] = 0x24; + data[16] = 0x87; + } else { + data[13] = 0x99; + data[14] = 0x00; + data[15] = 0x24; + data[16] = 0x87; + } + + helene_write_regs(priv, 0x68, data, 17); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct helene_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Ter tuner", + .frequency_min = 1000000, + .frequency_max = 1200000000, + .frequency_step = 25000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + +static struct dvb_tuner_ops helene_tuner_ops_s = { + .info = { + .name = "Sony HELENE Sat tuner", + .frequency_min = 500000, + .frequency_max = 2500000, + .frequency_step = 1000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params_s, + .get_frequency = helene_get_frequency, +}; + +/* power-on tuner + * call once after reset + */ +static int helene_x_pon(struct helene_priv *priv) +{ + /* RFIN matching in power save (terrestrial) = ACTIVE */ + /* RFIN matching in power save (satellite) = ACTIVE */ + u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 }; + /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */ + u8 dataS[] = { 0x05, 0x06 }; + u8 cdata[] = {0x7A, 0x01}; + u8 data[20]; + u8 rdata[2]; + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + helene_write_reg(priv, 0x67, dataT[3]); + helene_write_reg(priv, 0x43, dataS[1]); + helene_write_regs(priv, 0x5E, dataT, 3); + helene_write_reg(priv, 0x0C, dataS[0]); + + /* Initial setting for internal logic block */ + helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); + + /* 0x81 - 0x94 */ + data[0] = 0x18; /* xtal 24 MHz */ + data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ + data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ + data[3] = 0x80; /* REFOUT signal output 500mVpp */ + data[4] = 0x00; /* GPIO settings */ + data[5] = 0x00; /* GPIO settings */ + data[6] = 0xC4; /* Clock enable for internal logic block */ + data[7] = 0x40; /* Start CPU boot-up */ + data[8] = 0x10; /* For burst-write */ + + /* Setting for internal RFAGC */ + data[9] = 0x00; + data[10] = 0x45; + data[11] = 0x75; + + data[12] = 0x07; /* Setting for analog block */ + + /* Initial setting for internal analog block */ + data[13] = 0x1C; + data[14] = 0x3F; + data[15] = 0x02; + data[16] = 0x10; + data[17] = 0x20; + data[18] = 0x0A; + data[19] = 0x00; + + helene_write_regs(priv, 0x81, data, sizeof(data)); + + /* Setting for internal RFAGC */ + helene_write_reg(priv, 0x9B, 0x00); + + msleep(20); + + /* Check CPU_STT/CPU_ERR */ + helene_read_regs(priv, 0x1A, rdata, sizeof(rdata)); + + if (rdata[0] != 0x00) { + dev_err(&priv->i2c->dev, + "HELENE tuner CPU error 0x%x\n", rdata[0]); + return -EIO; + } + + /* VCO current setting */ + cdata[0] = 0x90; + cdata[1] = 0x06; + helene_write_regs(priv, 0x17, cdata, sizeof(cdata)); + msleep(20); + helene_read_reg(priv, 0x19, data); + helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F)); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x00); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + /* Load capacitance control setting for crystal oscillator */ + helene_write_reg(priv, 0x80, 0x01); + + /* Satellite initial setting */ + cdata[0] = 0x07; + cdata[1] = 0x00; + helene_write_regs(priv, 0x41, cdata, sizeof(cdata)); + + dev_info(&priv->i2c->dev, + "HELENE tuner x_pon done\n"); + + return 0; +} + +struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach_s); + +struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach); + +MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); +MODULE_AUTHOR("Abylay Ospan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h new file mode 100644 index 000000000000..e1b9224cfc55 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.h @@ -0,0 +1,79 @@ +/* + * helene.h + * + * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DVB_HELENE_H__ +#define __DVB_HELENE_H__ + +#include +#include +#include + +enum helene_xtal { + SONY_HELENE_XTAL_16000, /* 16 MHz */ + SONY_HELENE_XTAL_20500, /* 20.5 MHz */ + SONY_HELENE_XTAL_24000, /* 24 MHz */ + SONY_HELENE_XTAL_41000 /* 41 MHz */ +}; + +/** + * struct helene_config - the configuration of 'Helene' tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct helene_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); + enum helene_xtal xtal; +}; + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 000606af70f7..a98bca5270d9 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -66,7 +66,7 @@ static int horus3a_write_regs(struct horus3a_priv *priv, } }; - if (len + 1 >= sizeof(buf)) { + if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; @@ -272,24 +272,6 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (fc_lpf > 36) fc_lpf = 36; } else if (p->delivery_system == SYS_DVBS2) { - int rolloff; - - switch (p->rolloff) { - case ROLLOFF_35: - rolloff = 35; - break; - case ROLLOFF_25: - rolloff = 25; - break; - case ROLLOFF_20: - rolloff = 20; - break; - case ROLLOFF_AUTO: - default: - dev_err(&priv->i2c->dev, - "horus3a: auto roll-off is not supported\n"); - return -EINVAL; - } /* * SR <= 4.5: * fc_lpf = 5 @@ -302,11 +284,9 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (symbol_rate <= 4500) fc_lpf = 5; else if (symbol_rate <= 10000) - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (200 + rolloff), 200000); + fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000); else - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (100 + rolloff), 200000) + 5; + fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5); /* 5 <= fc_lpf <= 36 is valid */ if (fc_lpf > 36) fc_lpf = 36; diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 5557ef8fc704..e0fe5bc9dbce 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -306,8 +306,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) const struct m88ds3103_reg_val *init; u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */ u8 buf[3]; - u16 u16tmp, divide_ratio = 0; - u32 tuner_frequency, target_mclk; + u16 u16tmp; + u32 tuner_frequency_khz, target_mclk; s32 s32tmp; dev_dbg(&client->dev, @@ -344,7 +344,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } if (fe->ops.tuner_ops.get_frequency) { - ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency); + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz); if (ret) goto err; } else { @@ -353,20 +353,20 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) * actual frequency used. Carrier offset calculation is not * valid. */ - tuner_frequency = c->frequency; + tuner_frequency_khz = c->frequency; } /* select M88RS6000 demod main mclk and ts mclk from tuner die. */ if (dev->chip_id == M88RS6000_CHIP_ID) { if (c->symbol_rate > 45010000) - dev->mclk_khz = 110250; + dev->mclk = 110250000; else - dev->mclk_khz = 96000; + dev->mclk = 96000000; if (c->delivery_system == SYS_DVBS) - target_mclk = 96000; + target_mclk = 96000000; else - target_mclk = 144000; + target_mclk = 144000000; /* Enable demod clock path */ ret = regmap_write(dev->regmap, 0x06, 0x00); @@ -375,7 +375,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) usleep_range(10000, 20000); } else { /* set M88DS3103 mclk and ts mclk. */ - dev->mclk_khz = 96000; + dev->mclk = 96000000; switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: @@ -385,14 +385,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) case M88DS3103_TS_PARALLEL: case M88DS3103_TS_CI: if (c->delivery_system == SYS_DVBS) - target_mclk = 96000; + target_mclk = 96000000; else { if (c->symbol_rate < 18000000) - target_mclk = 96000; + target_mclk = 96000000; else if (c->symbol_rate < 28000000) - target_mclk = 144000; + target_mclk = 144000000; else - target_mclk = 192000; + target_mclk = 192000000; } break; default: @@ -402,15 +402,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } switch (target_mclk) { - case 96000: + case 96000000: u8tmp1 = 0x02; /* 0b10 */ u8tmp2 = 0x01; /* 0b01 */ break; - case 144000: + case 144000000: u8tmp1 = 0x00; /* 0b00 */ u8tmp2 = 0x01; /* 0b01 */ break; - case 192000: + case 192000000: u8tmp1 = 0x03; /* 0b11 */ u8tmp2 = 0x00; /* 0b00 */ break; @@ -464,8 +464,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } if (dev->chip_id == M88RS6000_CHIP_ID) { - if ((c->delivery_system == SYS_DVBS2) - && ((c->symbol_rate / 1000) <= 5000)) { + if (c->delivery_system == SYS_DVBS2 && + c->symbol_rate <= 5000000) { ret = regmap_write(dev->regmap, 0xc0, 0x04); if (ret) goto err; @@ -522,37 +522,25 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); if (ret) goto err; - u8tmp1 = 0; - u8tmp2 = 0; + u16tmp = 0; + u8tmp1 = 0x3f; + u8tmp2 = 0x3f; break; default: - if (dev->cfg->ts_clk) { - divide_ratio = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); - u8tmp1 = divide_ratio / 2; - u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); - } + u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); + u8tmp1 = u16tmp / 2 - 1; + u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1; } - dev_dbg(&client->dev, - "target_mclk=%d ts_clk=%d divide_ratio=%d\n", - target_mclk, dev->cfg->ts_clk, divide_ratio); + dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n", + target_mclk, dev->cfg->ts_clk, u16tmp); - u8tmp1--; - u8tmp2--; /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */ - u8tmp1 &= 0x3f; /* u8tmp2[5:0] => ea[5:0] */ - u8tmp2 &= 0x3f; - - ret = regmap_bulk_read(dev->regmap, 0xfe, &u8tmp, 1); + u8tmp = (u8tmp1 >> 2) & 0x0f; + ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp); if (ret) goto err; - - u8tmp = ((u8tmp & 0xf0) << 0) | u8tmp1 >> 2; - ret = regmap_write(dev->regmap, 0xfe, u8tmp); - if (ret) - goto err; - u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; ret = regmap_write(dev->regmap, 0xea, u8tmp); if (ret) @@ -581,7 +569,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, dev->mclk_khz / 2); + u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); buf[0] = (u16tmp >> 0) & 0xff; buf[1] = (u16tmp >> 8) & 0xff; ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); @@ -601,13 +589,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; dev_dbg(&client->dev, "carrier offset=%d\n", - (tuner_frequency - c->frequency)); - - s32tmp = 0x10000 * (tuner_frequency - c->frequency); - s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz); - if (s32tmp < 0) - s32tmp += 0x10000; + (tuner_frequency_khz - c->frequency)); + /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */ + s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000); buf[0] = (s32tmp >> 0) & 0xff; buf[1] = (s32tmp >> 8) & 0xff; ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); @@ -635,10 +621,10 @@ static int m88ds3103_init(struct dvb_frontend *fe) struct m88ds3103_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, len, remaining; + int ret, len, rem; unsigned int utmp; - const struct firmware *fw = NULL; - u8 *fw_file; + const struct firmware *firmware; + const char *name; dev_dbg(&client->dev, "\n"); @@ -664,7 +650,7 @@ static int m88ds3103_init(struct dvb_frontend *fe) dev_dbg(&client->dev, "firmware=%02x\n", utmp); if (utmp) - goto skip_fw_download; + goto warm; /* global reset, global diseqc reset, golbal fec reset */ ret = regmap_write(dev->regmap, 0x07, 0xe0); @@ -679,52 +665,47 @@ static int m88ds3103_init(struct dvb_frontend *fe) m88ds3103_ops.info.name); if (dev->chip_id == M88RS6000_CHIP_ID) - fw_file = M88RS6000_FIRMWARE; + name = M88RS6000_FIRMWARE; else - fw_file = M88DS3103_FIRMWARE; + name = M88DS3103_FIRMWARE; /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &client->dev); + ret = request_firmware(&firmware, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmware file '%s' not found\n", fw_file); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } - dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); ret = regmap_write(dev->regmap, 0xb2, 0x01); if (ret) - goto error_fw_release; - - for (remaining = fw->size; remaining > 0; - remaining -= (dev->cfg->i2c_wr_max - 1)) { - len = remaining; - if (len > (dev->cfg->i2c_wr_max - 1)) - len = (dev->cfg->i2c_wr_max - 1); + goto err_release_firmware; + for (rem = firmware->size; rem > 0; rem -= (dev->cfg->i2c_wr_max - 1)) { + len = min(dev->cfg->i2c_wr_max - 1, rem); ret = regmap_bulk_write(dev->regmap, 0xb0, - &fw->data[fw->size - remaining], len); + &firmware->data[firmware->size - rem], + len); if (ret) { - dev_err(&client->dev, "firmware download failed=%d\n", + dev_err(&client->dev, "firmware download failed %d\n", ret); - goto error_fw_release; + goto err_release_firmware; } } ret = regmap_write(dev->regmap, 0xb2, 0x00); if (ret) - goto error_fw_release; + goto err_release_firmware; - release_firmware(fw); - fw = NULL; + release_firmware(firmware); ret = regmap_read(dev->regmap, 0xb9, &utmp); if (ret) goto err; if (!utmp) { + ret = -EINVAL; dev_info(&client->dev, "firmware did not run\n"); - ret = -EFAULT; goto err; } @@ -733,7 +714,7 @@ static int m88ds3103_init(struct dvb_frontend *fe) dev_info(&client->dev, "firmware version: %X.%X\n", (utmp >> 4) & 0xf, (utmp >> 0 & 0xf)); -skip_fw_download: +warm: /* warm state */ dev->warm = true; @@ -746,8 +727,8 @@ static int m88ds3103_init(struct dvb_frontend *fe) c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; return 0; -error_fw_release: - release_firmware(fw); +err_release_firmware: + release_firmware(firmware); err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; @@ -952,8 +933,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe, if (ret) goto err; - c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) * - dev->mclk_khz * 1000 / 0x10000; + c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000); return 0; err: @@ -1119,8 +1099,9 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, #define SEND_MASTER_CMD_TIMEOUT 120 timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT); - /* DiSEqC message typical period is 54 ms */ - usleep_range(50000, 54000); + /* DiSEqC message period is 13.5 ms per byte */ + utmp = diseqc_cmd->msg_len * 13500; + usleep_range(utmp - 4000, utmp); for (utmp = 1; !time_after(jiffies, timeout) && utmp;) { ret = regmap_read(dev->regmap, 0xa1, &utmp); @@ -1395,7 +1376,7 @@ static int m88ds3103_probe(struct i2c_client *client, dev->config.clock = pdata->clk; dev->config.i2c_wr_max = pdata->i2c_wr_max; dev->config.ts_mode = pdata->ts_mode; - dev->config.ts_clk = pdata->ts_clk; + dev->config.ts_clk = pdata->ts_clk * 1000; dev->config.ts_clk_pol = pdata->ts_clk_pol; dev->config.spec_inv = pdata->spec_inv; dev->config.agc_inv = pdata->agc_inv; @@ -1446,6 +1427,11 @@ static int m88ds3103_probe(struct i2c_client *client, goto err_kfree; } + if (!pdata->ts_clk) { + ret = -EINVAL; + goto err_kfree; + } + /* 0x29 register is defined differently for m88rs6000. */ /* set internal tuner address to 0x21 */ if (dev->chip_id == M88RS6000_CHIP_ID) diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index d78e467295d2..07f20c269c67 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -27,7 +27,6 @@ #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" #define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" -#define M88DS3103_MCLK_KHZ 96000 #define M88RS6000_CHIP_ID 0x74 #define M88DS3103_CHIP_ID 0x70 @@ -46,7 +45,7 @@ struct m88ds3103_dev { /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ - s32 mclk_khz; + s32 mclk; u64 post_bit_error; u64 post_bit_count; }; diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index a09b12313a73..ef79a4ec31e2 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -609,7 +609,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - enum fe_status status; + enum fe_status status = 0; int i, ret = 0; u32 tuner_freq; s16 offset = 0; diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index fb88dddaf3a3..41325328a22e 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -301,10 +301,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status) *status = 0; - val = mb86a20s_readreg(state, 0x0a) & 0xf; + val = mb86a20s_readreg(state, 0x0a); if (val < 0) return val; + val &= 0xf; if (val >= 2) *status |= FE_HAS_SIGNAL; diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/media/dvb-frontends/mn88472.c similarity index 58% rename from drivers/staging/media/mn88472/mn88472.c rename to drivers/media/dvb-frontends/mn88472.c index 7ea749cf19f9..18fb2df1e2bd 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -17,28 +17,90 @@ #include "mn88472_priv.h" static int mn88472_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *s) + struct dvb_frontend_tune_settings *s) { - s->min_delay_ms = 800; + s->min_delay_ms = 1000; return 0; } +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x7f, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x92, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x84, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x08) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static int mn88472_set_frontend(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; - u32 if_frequency = 0; - u64 tmp; - u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; + u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; + u8 reg_bank0_d6_val; dev_dbg(&client->dev, - "delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n", - c->delivery_system, c->modulation, - c->frequency, c->symbol_rate, c->inversion); + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); - if (!dev->warm) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_DVBT: delivery_system_val = 0x02; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x1f; + reg_bank0_d4_val = 0x0a; + reg_bank0_d6_val = 0x48; break; case SYS_DVBT2: delivery_system_val = 0x03; + reg_bank0_b4_val = 0xf6; + reg_bank0_cd_val = 0x01; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x46; break; case SYS_DVBC_ANNEX_A: delivery_system_val = 0x04; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x17; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x48; break; default: ret = -EINVAL; goto err; } - if (c->bandwidth_hz <= 5000000) { - memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7); - bw_val2 = 0x03; - } else if (c->bandwidth_hz <= 6000000) { - /* IF 3570000 Hz, BW 6000000 Hz */ - memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); - bw_val2 = 0x02; - } else if (c->bandwidth_hz <= 7000000) { - /* IF 4570000 Hz, BW 7000000 Hz */ - memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); - bw_val2 = 0x01; - } else if (c->bandwidth_hz <= 8000000) { - /* IF 4570000 Hz, BW 8000000 Hz */ - memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); - bw_val2 = 0x00; - } else { - ret = -EINVAL; - goto err; + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 5000000: + bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; + bandwidth_val = 0x03; + break; + case 6000000: + bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; + bandwidth_val = 0x02; + break; + case 7000000: + bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; + bandwidth_val = 0x01; + break; + case 8000000: + bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; + bandwidth_val = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + bandwidth_vals_ptr = NULL; + bandwidth_val = 0x00; + break; + default: + break; } - /* program tuner */ + /* Program tuner */ if (fe->ops.tuner_ops.set_params) { ret = fe->ops.tuner_ops.set_params(fe); if (ret) @@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) goto err; dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); - } - - /* Calculate IF registers ( (1<<24)*IF / Xtal ) */ - tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), - dev->xtal); - if_val[0] = (tmp >> 16) & 0xff; - if_val[1] = (tmp >> 8) & 0xff; - if_val[2] = (tmp >> 0) & 0xff; - - ret = regmap_write(dev->regmap[2], 0xfb, 0x13); - ret = regmap_write(dev->regmap[2], 0xef, 0x13); - ret = regmap_write(dev->regmap[2], 0xf9, 0x13); - if (ret) + } else { + ret = -EINVAL; goto err; + } ret = regmap_write(dev->regmap[2], 0x00, 0x66); if (ret) @@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x04, bw_val2); + ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); if (ret) goto err; - for (i = 0; i < sizeof(if_val); i++) { - ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); + /* IF */ + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + for (i = 0; i < 3; i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); if (ret) goto err; } - for (i = 0; i < sizeof(bw_val); i++) { - ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]); - if (ret) - goto err; + /* Bandwidth */ + if (bandwidth_vals_ptr) { + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + bandwidth_vals_ptr[i]); + if (ret) + goto err; + } } + ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); + if (ret) + goto err; + switch (c->delivery_system) { case SYS_DVBT: ret = regmap_write(dev->regmap[0], 0x07, 0x26); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x1f); - ret = regmap_write(dev->regmap[0], 0xd4, 0x0a); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x00, 0xba); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x01, 0x13); if (ret) goto err; break; case SYS_DVBT2: ret = regmap_write(dev->regmap[2], 0x2b, 0x13); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x4f, 0x05); + if (ret) + goto err; ret = regmap_write(dev->regmap[1], 0xf6, 0x05); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0xf6); - ret = regmap_write(dev->regmap[0], 0xcd, 0x01); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x46); - ret = regmap_write(dev->regmap[2], 0x30, 0x80); - ret = regmap_write(dev->regmap[2], 0x32, 0x00); if (ret) goto err; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_write(dev->regmap[0], 0xb0, 0x0b); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x17); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); - ret = regmap_write(dev->regmap[1], 0x00, 0xb0); + ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); if (ret) goto err; break; - default: - ret = -EINVAL; - goto err; - } - - ret = regmap_write(dev->regmap[0], 0x46, 0x00); - ret = regmap_write(dev->regmap[0], 0xae, 0x00); - - switch (dev->ts_mode) { - case SERIAL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x1d); - break; - case PARALLEL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x00); + case SYS_DVBC_ANNEX_A: break; default: - dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode); - ret = -EINVAL; - goto err; - } - - switch (dev->ts_clock) { - case VARIABLE_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); break; - case FIXED_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe1); - break; - default: - dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock); - ret = -EINVAL; - goto err; } - /* Reset demod */ + /* Reset FSM */ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); if (ret) goto err; - dev->delivery_system = c->delivery_system; - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - unsigned int utmp; - int lock = 0; - - *status = 0; - - if (!dev->warm) { - ret = -EAGAIN; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - ret = regmap_read(dev->regmap[0], 0x7F, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x09) - lock = 1; - break; - case SYS_DVBT2: - ret = regmap_read(dev->regmap[2], 0x92, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x07) - *status |= FE_HAS_SIGNAL; - if ((utmp & 0xF) >= 0x0a) - *status |= FE_HAS_CARRIER; - if ((utmp & 0xF) >= 0x0d) - *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_read(dev->regmap[1], 0x84, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x08) - lock = 1; - break; - default: - ret = -EINVAL; - goto err; - } - - if (lock) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_LOCK; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); - int ret, len, remaining; - const struct firmware *fw = NULL; - u8 *fw_file = MN88472_FIRMWARE; - unsigned int tmp; + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name = MN88472_FIRMWARE; dev_dbg(&client->dev, "\n"); - /* set cold state by default */ - dev->warm = false; - - /* power on */ + /* Power up */ ret = regmap_write(dev->regmap[2], 0x05, 0x00); if (ret) goto err; - - ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2); + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); if (ret) goto err; - - /* check if firmware is already running */ - ret = regmap_read(dev->regmap[0], 0xf5, &tmp); + ret = regmap_write(dev->regmap[2], 0x0c, 0x00); if (ret) goto err; - if (!(tmp & 0x1)) { - dev_info(&client->dev, "firmware already running\n"); - dev->warm = true; - return 0; - } + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &utmp); + if (ret) + goto err; + if (!(utmp & 0x01)) + goto warm; - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &client->dev); + ret = request_firmware(&firmware, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", - fw_file); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } - dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); ret = regmap_write(dev->regmap[0], 0xf5, 0x03); if (ret) - goto firmware_release; - - for (remaining = fw->size; remaining > 0; - remaining -= (dev->i2c_wr_max - 1)) { - len = remaining; - if (len > (dev->i2c_wr_max - 1)) - len = dev->i2c_wr_max - 1; + goto err_release_firmware; + for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { + len = min(dev->i2c_write_max - 1, rem); ret = regmap_bulk_write(dev->regmap[0], 0xf6, - &fw->data[fw->size - remaining], len); + &firmware->data[firmware->size - rem], + len); if (ret) { - dev_err(&client->dev, - "firmware download failed=%d\n", ret); - goto firmware_release; + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; } } - /* parity check of firmware */ - ret = regmap_read(dev->regmap[0], 0xf8, &tmp); - if (ret) { - dev_err(&client->dev, - "parity reg read failed=%d\n", ret); - goto firmware_release; - } - if (tmp & 0x10) { - dev_err(&client->dev, - "firmware parity check failed=0x%x\n", tmp); - goto firmware_release; + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &utmp); + if (ret) + goto err_release_firmware; + if (utmp & 0x10) { + ret = -EINVAL; + dev_err(&client->dev, "firmware did not run\n"); + goto err_release_firmware; } - dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp); ret = regmap_write(dev->regmap[0], 0xf5, 0x00); if (ret) - goto firmware_release; + goto err_release_firmware; + + release_firmware(firmware); +warm: + /* TS config */ + switch (dev->ts_mode) { + case SERIAL_TS_MODE: + utmp = 0x1d; + break; + case PARALLEL_TS_MODE: + utmp = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[2], 0x08, utmp); + if (ret) + goto err; - release_firmware(fw); - fw = NULL; + switch (dev->ts_clk) { + case VARIABLE_TS_CLOCK: + utmp = 0xe3; + break; + case FIXED_TS_CLOCK: + utmp = 0xe1; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[0], 0xd9, utmp); + if (ret) + goto err; - /* warm state */ - dev->warm = true; + dev->active = true; return 0; -firmware_release: - release_firmware(fw); +err_release_firmware: + release_firmware(firmware); err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; @@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); - /* power off */ + /* Power down */ + ret = regmap_write(dev->regmap[2], 0x0c, 0x30); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x0b, 0x30); - if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) goto err; - dev->delivery_system = SYS_UNDEFINED; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = { .read_status = mn88472_read_status, }; +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + static int mn88472_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { - struct mn88472_config *config = client->dev.platform_data; + struct mn88472_config *pdata = client->dev.platform_data; struct mn88472_dev *dev; int ret; unsigned int utmp; @@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client, dev_dbg(&client->dev, "\n"); - /* Caller really need to provide pointer for frontend we create. */ - if (config->fe == NULL) { - dev_err(&client->dev, "frontend pointer not defined\n"); - ret = -EINVAL; - goto err; - } - dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err; } - dev->i2c_wr_max = config->i2c_wr_max; - dev->xtal = config->xtal; - dev->ts_mode = config->ts_mode; - dev->ts_clock = config->ts_clock; + dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; + dev->clk = pdata->xtal; + dev->ts_mode = pdata->ts_mode; + dev->ts_clk = pdata->ts_clock; dev->client[0] = client; dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); if (IS_ERR(dev->regmap[0])) { @@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client, goto err_kfree; } - /* check demod answers to I2C */ - ret = regmap_read(dev->regmap[0], 0x00, &utmp); + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[0], 0xff, &utmp); if (ret) goto err_regmap_0_regmap_exit; + dev_dbg(&client->dev, "chip id=%02x\n", utmp); + + if (utmp != 0x02) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + /* - * Chip has three I2C addresses for different register pages. Used + * Chip has three I2C addresses for different register banks. Used * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, - * 0x1a and 0x1c, in order to get own I2C client for each register page. + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. */ dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); if (!dev->client[1]) { @@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client, } i2c_set_clientdata(dev->client[2], dev); - /* create dvb_frontend */ + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->fe = &dev->fe; + *pdata->fe = &dev->fe; i2c_set_clientdata(client, dev); - dev_info(&client->dev, "Panasonic MN88472 successfully attached\n"); - return 0; + /* Setup callbacks */ + pdata->get_dvb_frontend = mn88472_get_dvb_frontend; + dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: @@ -561,11 +597,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table); static struct i2c_driver mn88472_driver = { .driver = { - .name = "mn88472", + .name = "mn88472", + .suppress_bind_attrs = true, }, - .probe = mn88472_probe, - .remove = mn88472_remove, - .id_table = mn88472_id_table, + .probe = mn88472_probe, + .remove = mn88472_remove, + .id_table = mn88472_id_table, }; module_i2c_driver(mn88472_driver); diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h index 095294d292f3..323632523876 100644 --- a/drivers/media/dvb-frontends/mn88472.h +++ b/drivers/media/dvb-frontends/mn88472.h @@ -19,23 +19,33 @@ #include -enum ts_clock { - VARIABLE_TS_CLOCK, - FIXED_TS_CLOCK, -}; +/** + * struct mn88472_config - Platform data for the mn88472 driver + * @xtal: Clock frequency. + * @ts_mode: TS mode. + * @ts_clock: TS clock config. + * @i2c_wr_max: Max number of bytes driver writes to I2C at once. + * @get_dvb_frontend: Get DVB frontend. + */ -enum ts_mode { - SERIAL_TS_MODE, - PARALLEL_TS_MODE, -}; +/* Define old names for backward compatibility */ +#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE +#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED +#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL +#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL struct mn88472_config { - /* - * Max num of bytes given I2C adapter could write at once. - * Default: none - */ - u16 i2c_wr_max; + unsigned int xtal; + +#define MN88472_TS_MODE_SERIAL 0 +#define MN88472_TS_MODE_PARALLEL 1 + int ts_mode; +#define MN88472_TS_CLK_FIXED 0 +#define MN88472_TS_CLK_VARIABLE 1 + int ts_clock; + + u16 i2c_wr_max; /* Everything after that is returned by the driver. */ @@ -43,14 +53,7 @@ struct mn88472_config { * DVB frontend. */ struct dvb_frontend **fe; - - /* - * Xtal frequency. - * Hz - */ - u32 xtal; - int ts_mode; - int ts_clock; + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); }; #endif diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/media/dvb-frontends/mn88472_priv.h similarity index 88% rename from drivers/staging/media/mn88472/mn88472_priv.h rename to drivers/media/dvb-frontends/mn88472_priv.h index 1a0de9e46b66..cdf2597a25d1 100644 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ b/drivers/media/dvb-frontends/mn88472_priv.h @@ -28,12 +28,11 @@ struct mn88472_dev { struct i2c_client *client[3]; struct regmap *regmap[3]; struct dvb_frontend fe; - u16 i2c_wr_max; - enum fe_delivery_system delivery_system; - bool warm; /* FW running */ - u32 xtal; - int ts_mode; - int ts_clock; + u16 i2c_write_max; + unsigned int clk; + unsigned int active:1; + unsigned int ts_mode:1; + unsigned int ts_clk:1; }; #endif diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 6c5d592161d4..451974a1d7ed 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -330,7 +330,7 @@ static int mn88473_init(struct dvb_frontend *fe) /* Request the firmware, this will block and timeout */ ret = request_firmware(&fw, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", name); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } @@ -536,7 +536,7 @@ static int mn88473_probe(struct i2c_client *client, /* Sleep because chip is active by default */ ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) - goto err_client_2_i2c_unregister_device; + goto err_regmap_2_regmap_exit; /* Create dvb frontend */ memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); @@ -547,7 +547,8 @@ static int mn88473_probe(struct i2c_client *client, dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); return 0; - +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index d25d1e0cd4ca..87226056f226 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -135,8 +135,6 @@ static int rtl2830_init(struct dvb_frontend *fe) c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; c->post_bit_count.len = 1; c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - /* start statistics polling */ - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); dev->sleeping = false; @@ -152,8 +150,6 @@ static int rtl2830_sleep(struct dvb_frontend *fe) struct rtl2830_dev *dev = i2c_get_clientdata(client); dev->sleeping = true; - /* stop statistics polling */ - cancel_delayed_work_sync(&dev->stat_work); dev->fe_status = 0; return 0; @@ -396,8 +392,10 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct i2c_client *client = fe->demodulator_priv; struct rtl2830_dev *dev = i2c_get_clientdata(client); - int ret; - u8 u8tmp; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, stmp; + unsigned int utmp; + u8 u8tmp, buf[2]; *status = 0; @@ -419,6 +417,89 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status) dev->fe_status = *status; + /* Signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + /* Read IF AGC */ + ret = rtl2830_bulk_read(client, 0x359, buf, 2); + if (ret) + goto err; + + stmp = buf[0] << 8 | buf[1] << 0; + stmp = sign_extend32(stmp, 13); + utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff); + + dev_dbg(&client->dev, "IF AGC=%d\n", stmp); + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = utmp; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned int hierarchy, constellation; + #define CONSTELLATION_NUM 3 + #define HIERARCHY_NUM 4 + static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + {70705899, 70705899, 70705899, 70705899}, + {82433173, 82433173, 87483115, 94445660}, + {92888734, 92888734, 95487525, 99770748}, + }; + + ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1); + if (ret) + goto err; + + constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err; + + hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err; + + ret = rtl2830_bulk_read(client, 0x40c, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) + stmp = (constant[constellation][hierarchy] - + intlog10(utmp)) / ((1 << 24) / 10000); + else + stmp = 0; + + dev_dbg(&client->dev, "CNR raw=%u\n", utmp); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = stmp; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + ret = rtl2830_bulk_read(client, 0x34e, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += utmp; + dev->post_bit_count += 1000000; + + dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return ret; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -503,109 +584,6 @@ static struct dvb_frontend_ops rtl2830_ops = { .read_signal_strength = rtl2830_read_signal_strength, }; -static void rtl2830_stat_work(struct work_struct *work) -{ - struct rtl2830_dev *dev = container_of(work, struct rtl2830_dev, stat_work.work); - struct i2c_client *client = dev->client; - struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; - int ret, tmp; - u8 u8tmp, buf[2]; - u16 u16tmp; - - dev_dbg(&client->dev, "\n"); - - /* signal strength */ - if (dev->fe_status & FE_HAS_SIGNAL) { - struct {signed int x:14; } s; - - /* read IF AGC */ - ret = rtl2830_bulk_read(client, 0x359, buf, 2); - if (ret) - goto err; - - u16tmp = buf[0] << 8 | buf[1] << 0; - u16tmp &= 0x3fff; /* [13:0] */ - tmp = s.x = u16tmp; /* 14-bit bin to 2 complement */ - u16tmp = clamp_val(-4 * tmp + 32767, 0x0000, 0xffff); - - dev_dbg(&client->dev, "IF AGC=%d\n", tmp); - - c->strength.stat[0].scale = FE_SCALE_RELATIVE; - c->strength.stat[0].uvalue = u16tmp; - } else { - c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - } - - /* CNR */ - if (dev->fe_status & FE_HAS_VITERBI) { - unsigned hierarchy, constellation; - #define CONSTELLATION_NUM 3 - #define HIERARCHY_NUM 4 - static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { - {70705899, 70705899, 70705899, 70705899}, - {82433173, 82433173, 87483115, 94445660}, - {92888734, 92888734, 95487525, 99770748}, - }; - - ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1); - if (ret) - goto err; - - constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ - if (constellation > CONSTELLATION_NUM - 1) - goto err_schedule_delayed_work; - - hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ - if (hierarchy > HIERARCHY_NUM - 1) - goto err_schedule_delayed_work; - - ret = rtl2830_bulk_read(client, 0x40c, buf, 2); - if (ret) - goto err; - - u16tmp = buf[0] << 8 | buf[1] << 0; - if (u16tmp) - tmp = (constant[constellation][hierarchy] - - intlog10(u16tmp)) / ((1 << 24) / 10000); - else - tmp = 0; - - dev_dbg(&client->dev, "CNR raw=%u\n", u16tmp); - - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = tmp; - } else { - c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - } - - /* BER */ - if (dev->fe_status & FE_HAS_LOCK) { - ret = rtl2830_bulk_read(client, 0x34e, buf, 2); - if (ret) - goto err; - - u16tmp = buf[0] << 8 | buf[1] << 0; - dev->post_bit_error += u16tmp; - dev->post_bit_count += 1000000; - - dev_dbg(&client->dev, "BER errors=%u total=1000000\n", u16tmp); - - c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[0].uvalue = dev->post_bit_error; - c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[0].uvalue = dev->post_bit_count; - } else { - c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - } - -err_schedule_delayed_work: - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); - return; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); -} - static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) { struct i2c_client *client = fe->demodulator_priv; @@ -851,7 +829,6 @@ static int rtl2830_probe(struct i2c_client *client, dev->client = client; dev->pdata = client->dev.platform_data; dev->sleeping = true; - INIT_DELAYED_WORK(&dev->stat_work, rtl2830_stat_work); dev->regmap = regmap_init(&client->dev, ®map_bus, client, ®map_config); if (IS_ERR(dev->regmap)) { @@ -904,9 +881,6 @@ static int rtl2830_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - /* stop statistics polling */ - cancel_delayed_work_sync(&dev->stat_work); - i2c_mux_del_adapters(dev->muxc); regmap_exit(dev->regmap); kfree(dev); @@ -922,7 +896,8 @@ MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); static struct i2c_driver rtl2830_driver = { .driver = { - .name = "rtl2830", + .name = "rtl2830", + .suppress_bind_attrs = true, }, .probe = rtl2830_probe, .remove = rtl2830_remove, diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h index da4909543da2..8ec4721d79ac 100644 --- a/drivers/media/dvb-frontends/rtl2830_priv.h +++ b/drivers/media/dvb-frontends/rtl2830_priv.h @@ -24,6 +24,7 @@ #include #include #include +#include struct rtl2830_dev { struct rtl2830_platform_data *pdata; @@ -33,7 +34,6 @@ struct rtl2830_dev { struct dvb_frontend fe; bool sleeping; unsigned long filters; - struct delayed_work stat_work; enum fe_status fe_status; u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ u64 post_bit_error; diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index bfb6beedd40b..0ced01f1012e 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -947,6 +947,8 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) goto err; } + dev->slave_ts = enable; + return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -960,7 +962,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) int ret; u8 u8tmp; - dev_dbg(&client->dev, "onoff=%d\n", onoff); + dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts); /* enable / disable PID filter */ if (onoff) @@ -968,7 +970,10 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) else u8tmp = 0x00; - ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); + if (dev->slave_ts) + ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp); + else + ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); if (ret) goto err; @@ -986,8 +991,8 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int ret; u8 buf[4]; - dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", - index, pid, onoff); + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n", + index, pid, onoff, dev->slave_ts); /* skip invalid PIDs (0x2000) */ if (pid > 0x1fff || index > 32) @@ -1003,14 +1008,22 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, buf[1] = (dev->filters >> 8) & 0xff; buf[2] = (dev->filters >> 16) & 0xff; buf[3] = (dev->filters >> 24) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4); + else + ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); if (ret) goto err; /* add PID */ buf[0] = (pid >> 8) & 0xff; buf[1] = (pid >> 0) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2); + else + ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); if (ret) goto err; @@ -1135,6 +1148,7 @@ MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); static struct i2c_driver rtl2832_driver = { .driver = { .name = "rtl2832", + .suppress_bind_attrs = true, }, .probe = rtl2832_probe, .remove = rtl2832_remove, diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index c1a8a69e9015..9a6d01a9c690 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -44,6 +44,7 @@ struct rtl2832_dev { bool sleeping; struct delayed_work i2c_gate_work; unsigned long filters; /* PID filter */ + bool slave_ts; }; struct rtl2832_reg_entry { diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 47a480a7d46c..6e22af36b637 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -452,7 +452,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh, /* Videobuf2 operations */ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) + unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); struct platform_device *pdev = dev->pdev; diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 108a069fa1ae..20b4a659e2e4 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -357,9 +357,7 @@ static int si2168_init(struct dvb_frontend *fe) struct si2168_dev *dev = i2c_get_clientdata(client); int ret, len, remaining; const struct firmware *fw; - const char *fw_name; struct si2168_cmd cmd; - unsigned int chip_id; dev_dbg(&client->dev, "\n"); @@ -371,7 +369,7 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - if (dev->fw_loaded) { + if (dev->warm) { /* resume */ memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8); cmd.wlen = 8; @@ -398,49 +396,14 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - /* query chip revision */ - memcpy(cmd.args, "\x02", 1); - cmd.wlen = 1; - cmd.rlen = 13; - ret = si2168_cmd_execute(client, &cmd); - if (ret) - goto err; - - chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | - cmd.args[4] << 0; - - #define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) - #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) - #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) - - switch (chip_id) { - case SI2168_A20: - fw_name = SI2168_A20_FIRMWARE; - break; - case SI2168_A30: - fw_name = SI2168_A30_FIRMWARE; - break; - case SI2168_B40: - fw_name = SI2168_B40_FIRMWARE; - break; - default: - dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n", - cmd.args[2], cmd.args[1], - cmd.args[3], cmd.args[4]); - ret = -EINVAL; - goto err; - } - - dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n", - cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_name, &client->dev); + ret = request_firmware(&fw, dev->firmware_name, &client->dev); if (ret) { /* fallback mechanism to handle old name for Si2168 B40 fw */ - if (chip_id == SI2168_B40) { - fw_name = SI2168_B40_FIRMWARE_FALLBACK; - ret = request_firmware(&fw, fw_name, &client->dev); + if (dev->chip_id == SI2168_CHIP_ID_B40) { + dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK; + ret = request_firmware(&fw, dev->firmware_name, + &client->dev); } if (ret == 0) { @@ -450,13 +413,13 @@ static int si2168_init(struct dvb_frontend *fe) } else { dev_err(&client->dev, "firmware file '%s' not found\n", - fw_name); + dev->firmware_name); goto err_release_firmware; } } dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_name); + dev->firmware_name); if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { /* firmware is in the new format */ @@ -511,8 +474,11 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - dev_info(&client->dev, "firmware version: %c.%c.%d\n", - cmd.args[6], cmd.args[7], cmd.args[8]); + dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 | + (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0; + dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); /* set ts mode */ memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); @@ -525,7 +491,7 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - dev->fw_loaded = true; + dev->warm = true; warm: dev->active = true; @@ -549,6 +515,10 @@ static int si2168_sleep(struct dvb_frontend *fe) dev->active = false; + /* Firmware B 4.0-11 or later loses warm state during sleep */ + if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) + dev->warm = false; + memcpy(cmd.args, "\x13", 1); cmd.wlen = 1; cmd.rlen = 0; @@ -653,6 +623,7 @@ static int si2168_probe(struct i2c_client *client, struct si2168_config *config = client->dev.platform_data; struct si2168_dev *dev; int ret; + struct si2168_cmd cmd; dev_dbg(&client->dev, "\n"); @@ -663,8 +634,56 @@ static int si2168_probe(struct i2c_client *client, goto err; } + i2c_set_clientdata(client, dev); mutex_init(&dev->i2c_mutex); + /* Initialize */ + memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); + cmd.wlen = 13; + cmd.rlen = 0; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + /* Power up */ + memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + /* Query chip revision */ + memcpy(cmd.args, "\x02", 1); + cmd.wlen = 1; + cmd.rlen = 13; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | + cmd.args[3] << 8 | cmd.args[4] << 0; + + switch (dev->chip_id) { + case SI2168_CHIP_ID_A20: + dev->firmware_name = SI2168_A20_FIRMWARE; + break; + case SI2168_CHIP_ID_A30: + dev->firmware_name = SI2168_A30_FIRMWARE; + break; + case SI2168_CHIP_ID_B40: + dev->firmware_name = SI2168_B40_FIRMWARE; + break; + default: + dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); + ret = -ENODEV; + goto err_kfree; + } + + dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 | + (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0; + /* create mux i2c adapter for tuner */ dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, I2C_MUX_LOCKED, @@ -686,11 +705,14 @@ static int si2168_probe(struct i2c_client *client, dev->ts_mode = config->ts_mode; dev->ts_clock_inv = config->ts_clock_inv; dev->ts_clock_gapped = config->ts_clock_gapped; - dev->fw_loaded = false; - i2c_set_clientdata(client, dev); + dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff); + dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); - dev_info(&client->dev, "Silicon Labs Si2168 successfully attached\n"); return 0; err_kfree: kfree(dev); @@ -723,7 +745,8 @@ MODULE_DEVICE_TABLE(i2c, si2168_id_table); static struct i2c_driver si2168_driver = { .driver = { - .name = "si2168", + .name = "si2168", + .suppress_bind_attrs = true, }, .probe = si2168_probe, .remove = si2168_remove, diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 8a1f36d2014d..7843ccb448a0 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -34,8 +34,14 @@ struct si2168_dev { struct dvb_frontend fe; enum fe_delivery_system delivery_system; enum fe_status fe_status; + #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) + #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) + #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + unsigned int chip_id; + unsigned int version; + const char *firmware_name; bool active; - bool fw_loaded; + bool warm; u8 ts_mode; bool ts_clock_inv; bool ts_clock_gapped; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 993dc50c12db..ce9006e10a30 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -209,6 +209,7 @@ config VIDEO_ADV7604 depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on GPIOLIB || COMPILE_TEST select HDMI + select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -218,10 +219,18 @@ config VIDEO_ADV7604 To compile this driver as a module, choose M here: the module will be called adv7604. +config VIDEO_ADV7604_CEC + bool "Enable Analog Devices ADV7604 CEC support" + depends on VIDEO_ADV7604 && MEDIA_CEC + ---help--- + When selected the adv7604 will support the optional + HDMI CEC feature. + config VIDEO_ADV7842 tristate "Analog Devices ADV7842 decoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API select HDMI + select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7842 video decoder. @@ -231,6 +240,13 @@ config VIDEO_ADV7842 To compile this driver as a module, choose M here: the module will be called adv7842. +config VIDEO_ADV7842_CEC + bool "Enable Analog Devices ADV7842 CEC support" + depends on VIDEO_ADV7842 && MEDIA_CEC + ---help--- + When selected the adv7842 will support the optional + HDMI CEC feature. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C @@ -447,6 +463,7 @@ config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API select HDMI + select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7511 video encoder. @@ -455,6 +472,13 @@ config VIDEO_ADV7511 To compile this driver as a module, choose M here: the module will be called adv7511. +config VIDEO_ADV7511_CEC + bool "Enable Analog Devices ADV7511 CEC support" + depends on VIDEO_ADV7511 && MEDIA_CEC + ---help--- + When selected the adv7511 will support the optional + HDMI CEC feature. + config VIDEO_AD9389B tristate "Analog Devices AD9389B encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 39271c35da48..6d7cad54a65d 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -33,6 +33,7 @@ #include #include #include +#include static int debug; module_param(debug, int, 0644); @@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2"); #define ADV7511_MIN_PIXELCLOCK 20000000 #define ADV7511_MAX_PIXELCLOCK 225000000 +#define ADV7511_MAX_ADDRS (3) + /* ********************************************************************** * @@ -90,12 +93,20 @@ struct adv7511_state { struct v4l2_ctrl_handler hdl; int chip_revision; u8 i2c_edid_addr; - u8 i2c_cec_addr; u8 i2c_pktmem_addr; + u8 i2c_cec_addr; + + struct i2c_client *i2c_cec; + struct cec_adapter *cec_adap; + u8 cec_addr[ADV7511_MAX_ADDRS]; + u8 cec_valid_addrs; + bool cec_enabled_adap; + /* Is the adv7511 powered on? */ bool power_on; /* Did we receive hotplug and rx-sense signals? */ bool have_monitor; + bool enabled_irq; /* timings from s_dv_timings */ struct v4l2_dv_timings dv_timings; u32 fmt_code; @@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client, return ret; } -static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) +static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) { struct adv7511_state *state = get_adv7511_state(sd); int i; @@ -242,6 +253,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) v4l2_err(sd, "%s: i2c read error\n", __func__); } +static inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + return i2c_smbus_read_byte_data(state->i2c_cec, reg); +} + +static int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val); + if (ret == 0) + return 0; + } + v4l2_err(sd, "%s: I2C Write Problem\n", __func__); + return ret; +} + +static inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, + u8 val) +{ + return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val); +} + static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) { struct adv7511_state *state = get_adv7511_state(sd); @@ -343,28 +382,20 @@ static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) } } -static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) +static void adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) { struct adv7511_state *state = get_adv7511_state(sd); - if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { - /* CE format, not IT */ - adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00); - } else { - /* IT format */ - adv7511_wr_and_or(sd, 0x57, 0x7f, 0x80); + + /* Only makes sense for RGB formats */ + if (state->fmt_code != MEDIA_BUS_FMT_RGB888_1X24) { + /* so just keep quantization */ + adv7511_csc_rgb_full2limit(sd, false); + return; } -} -static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) -{ switch (ctrl->val) { - default: - return -EINVAL; - break; - case V4L2_DV_RGB_RANGE_AUTO: { + case V4L2_DV_RGB_RANGE_AUTO: /* automatic */ - struct adv7511_state *state = get_adv7511_state(sd); - if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { /* CE format, RGB limited range (16-235) */ adv7511_csc_rgb_full2limit(sd, true); @@ -372,7 +403,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2 /* not CE format, RGB full range (0-255) */ adv7511_csc_rgb_full2limit(sd, false); } - } break; case V4L2_DV_RGB_RANGE_LIMITED: /* RGB limited range (16-235) */ @@ -383,7 +413,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2 adv7511_csc_rgb_full2limit(sd, false); break; } - return 0; } /* ------------------------------ CTRL OPS ------------------------------ */ @@ -400,8 +429,10 @@ static int adv7511_s_ctrl(struct v4l2_ctrl *ctrl) adv7511_wr_and_or(sd, 0xaf, 0xfd, ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); return 0; } - if (state->rgb_quantization_range_ctrl == ctrl) - return adv7511_set_rgb_quantization_mode(sd, ctrl); + if (state->rgb_quantization_range_ctrl == ctrl) { + adv7511_set_rgb_quantization_mode(sd, ctrl); + return 0; + } if (state->content_type_ctrl == ctrl) { u8 itc, cn; @@ -425,16 +456,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG static void adv7511_inv_register(struct v4l2_subdev *sd) { + struct adv7511_state *state = get_adv7511_state(sd); + v4l2_info(sd, "0x000-0x0ff: Main Map\n"); + if (state->i2c_cec) + v4l2_info(sd, "0x100-0x1ff: CEC Map\n"); } static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { + struct adv7511_state *state = get_adv7511_state(sd); + reg->size = 1; switch (reg->reg >> 8) { case 0: reg->val = adv7511_rd(sd, reg->reg & 0xff); break; + case 1: + if (state->i2c_cec) { + reg->val = adv7511_cec_read(sd, reg->reg & 0xff); + break; + } + /* fall through */ default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); @@ -445,10 +488,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { + struct adv7511_state *state = get_adv7511_state(sd); + switch (reg->reg >> 8) { case 0: adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); break; + case 1: + if (state->i2c_cec) { + adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff); + break; + } + /* fall through */ default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); @@ -536,6 +587,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); struct adv7511_state_edid *edid = &state->edid; + int i; static const char * const states[] = { "in reset", @@ -605,7 +657,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd) else v4l2_info(sd, "no timings set\n"); v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); + + if (state->i2c_cec == NULL) + return 0; + v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); + + v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? + "enabled" : "disabled"); + if (state->cec_enabled_adap) { + for (i = 0; i < ADV7511_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (is_valid) + v4l2_info(sd, "CEC Logical Address: 0x%x\n", + state->cec_addr[i]); + } + } v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); return 0; } @@ -663,15 +731,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) return true; } +#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) +static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct adv7511_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + + if (state->i2c_cec == NULL) + return -EIO; + + if (!state->cec_enabled_adap && enable) { + /* power up cec section */ + adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01); + /* legacy mode and clear all rx buffers */ + adv7511_cec_write(sd, 0x4a, 0x07); + adv7511_cec_write(sd, 0x4a, 0); + adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ + /* enabled irqs: */ + /* tx: ready */ + /* tx: arbitration lost */ + /* tx: retry timeout */ + /* rx: ready 1 */ + if (state->enabled_irq) + adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39); + } else if (state->cec_enabled_adap && !enable) { + if (state->enabled_irq) + adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00); + /* disable address mask 1-3 */ + adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00); + /* power down cec section */ + adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00); + state->cec_valid_addrs = 0; + } + state->cec_enabled_adap = enable; + return 0; +} + +static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +{ + struct adv7511_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + unsigned int i, free_idx = ADV7511_MAX_ADDRS; + + if (!state->cec_enabled_adap) + return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; + + if (addr == CEC_LOG_ADDR_INVALID) { + adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0); + state->cec_valid_addrs = 0; + return 0; + } + + for (i = 0; i < ADV7511_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (free_idx == ADV7511_MAX_ADDRS && !is_valid) + free_idx = i; + if (is_valid && state->cec_addr[i] == addr) + return 0; + } + if (i == ADV7511_MAX_ADDRS) { + i = free_idx; + if (i == ADV7511_MAX_ADDRS) + return -ENXIO; + } + state->cec_addr[i] = addr; + state->cec_valid_addrs |= 1 << i; + + switch (i) { + case 0: + /* enable address mask 0 */ + adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10); + /* set address for mask 0 */ + adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr); + break; + case 1: + /* enable address mask 1 */ + adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20); + /* set address for mask 1 */ + adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4); + break; + case 2: + /* enable address mask 2 */ + adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40); + /* set address for mask 1 */ + adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr); + break; + } + return 0; +} + +static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct adv7511_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + u8 len = msg->len; + unsigned int i; + + v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len); + + if (len > 16) { + v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); + return -EINVAL; + } + + /* + * The number of retries is the number of attempts - 1, but retry + * at least once. It's not clear if a value of 0 is allowed, so + * let's do at least one retry. + */ + adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4); + + /* blocking, clear cec tx irq status */ + adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38); + + /* write data */ + for (i = 0; i < len; i++) + adv7511_cec_write(sd, i, msg->msg[i]); + + /* set length (data + header) */ + adv7511_cec_write(sd, 0x10, len); + /* start transmit, enable tx */ + adv7511_cec_write(sd, 0x11, 0x01); + return 0; +} + +static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) { + v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); + return; + } + + if (tx_raw_status & 0x10) { + v4l2_dbg(1, debug, sd, + "%s: tx raw: arbitration lost\n", __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, + 1, 0, 0, 0); + return; + } + if (tx_raw_status & 0x08) { + u8 status; + u8 nack_cnt; + u8 low_drive_cnt; + + v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); + /* + * We set this status bit since this hardware performs + * retransmissions. + */ + status = CEC_TX_STATUS_MAX_RETRIES; + nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf; + if (nack_cnt) + status |= CEC_TX_STATUS_NACK; + low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4; + if (low_drive_cnt) + status |= CEC_TX_STATUS_LOW_DRIVE; + cec_transmit_done(state->cec_adap, status, + 0, nack_cnt, low_drive_cnt, 0); + return; + } + if (tx_raw_status & 0x20) { + v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); + return; + } +} + +static const struct cec_adap_ops adv7511_cec_adap_ops = { + .adap_enable = adv7511_cec_adap_enable, + .adap_log_addr = adv7511_cec_adap_log_addr, + .adap_transmit = adv7511_cec_adap_transmit, +}; +#endif + /* Enable interrupts */ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) { + struct adv7511_state *state = get_adv7511_state(sd); u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; u8 irqs_rd; int retries = 100; v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + if (state->enabled_irq == enable) + return; + state->enabled_irq = enable; + /* The datasheet says that the EDID ready interrupt should be disabled if there is no hotplug. */ if (!enable) @@ -679,6 +929,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) else if (adv7511_have_hotplug(sd)) irqs |= MASK_ADV7511_EDID_RDY_INT; + adv7511_wr_and_or(sd, 0x95, 0xc0, + (state->cec_enabled_adap && enable) ? 0x39 : 0x00); + /* * This i2c write can fail (approx. 1 in 1000 writes). But it * is essential that this register is correct, so retry it @@ -701,20 +954,53 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { u8 irq_status; + u8 cec_irq; /* disable interrupts to prevent a race condition */ adv7511_set_isr(sd, false); irq_status = adv7511_rd(sd, 0x96); + cec_irq = adv7511_rd(sd, 0x97); /* clear detected interrupts */ adv7511_wr(sd, 0x96, irq_status); + adv7511_wr(sd, 0x97, cec_irq); - v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status); + v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__, + irq_status, cec_irq); if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) adv7511_check_monitor_present_status(sd); if (irq_status & MASK_ADV7511_EDID_RDY_INT) adv7511_check_edid_status(sd); +#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) + if (cec_irq & 0x38) + adv_cec_tx_raw_status(sd, cec_irq); + + if (cec_irq & 1) { + struct adv7511_state *state = get_adv7511_state(sd); + struct cec_msg msg; + + msg.len = adv7511_cec_read(sd, 0x25) & 0x1f; + + v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__, + msg.len); + + if (msg.len > 16) + msg.len = 16; + + if (msg.len) { + u8 i; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = adv7511_cec_read(sd, i + 0x15); + + adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */ + adv7511_cec_write(sd, 0x4a, 0); + cec_received_msg(state->cec_adap, &msg); + } + } +#endif + /* enable interrupts */ adv7511_set_isr(sd, true); @@ -771,12 +1057,14 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, /* save timings */ state->dv_timings = *timings; + /* set h/vsync polarities */ + adv7511_wr_and_or(sd, 0x17, 0x9f, + ((timings->bt.polarities & V4L2_DV_VSYNC_POS_POL) ? 0 : 0x40) | + ((timings->bt.polarities & V4L2_DV_HSYNC_POS_POL) ? 0 : 0x20)); + /* update quantization range based on new dv_timings */ adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); - /* update AVI infoframe */ - adv7511_set_IT_content_AVI_InfoFrame(sd); - return 0; } @@ -956,8 +1244,6 @@ static int adv7511_enum_mbus_code(struct v4l2_subdev *sd, static void adv7511_fill_format(struct adv7511_state *state, struct v4l2_mbus_framefmt *format) { - memset(format, 0, sizeof(*format)); - format->width = state->dv_timings.bt.width; format->height = state->dv_timings.bt.height; format->field = V4L2_FIELD_NONE; @@ -972,6 +1258,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, if (format->pad != 0) return -EINVAL; + memset(&format->format, 0, sizeof(format->format)); adv7511_fill_format(state, &format->format); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1132,6 +1419,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2) | (itc << 7)); adv7511_wr_and_or(sd, 0x59, 0x0f, (yq << 6) | (cn << 4)); adv7511_wr_and_or(sd, 0x4a, 0xff, 1); + adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); return 0; } @@ -1183,6 +1471,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd) /* We failed to read the EDID, so send an event for this. */ ed.present = false; ed.segment = adv7511_rd(sd, 0xc4); + ed.phys_addr = CEC_PHYS_ADDR_INVALID; + cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0); } @@ -1406,13 +1696,16 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); state->edid.complete = true; - + ed.phys_addr = cec_get_edid_phys_addr(state->edid.data, + state->edid.segments * 256, + NULL); /* report when we have all segments but report only for segment 0 */ ed.present = true; ed.segment = 0; state->edid_detect_counter++; + cec_s_phys_addr(state->cec_adap, ed.phys_addr, false); v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); return ed.present; } @@ -1420,17 +1713,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) return false; } +static int adv7511_registered(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int err; + + err = cec_register_adapter(state->cec_adap); + if (err) + cec_delete_adapter(state->cec_adap); + return err; +} + +static void adv7511_unregistered(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + cec_unregister_adapter(state->cec_adap); +} + +static const struct v4l2_subdev_internal_ops adv7511_int_ops = { + .registered = adv7511_registered, + .unregistered = adv7511_unregistered, +}; + /* ----------------------------------------------------------------------- */ /* Setup ADV7511 */ static void adv7511_init_setup(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); struct adv7511_state_edid *edid = &state->edid; + u32 cec_clk = state->pdata.cec_clk; + u8 ratio; v4l2_dbg(1, debug, sd, "%s\n", __func__); /* clear all interrupts */ adv7511_wr(sd, 0x96, 0xff); + adv7511_wr(sd, 0x97, 0xff); /* * Stop HPD from resetting a lot of registers. * It might leave the chip in a partly un-initialized state, @@ -1442,6 +1761,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) adv7511_set_isr(sd, false); adv7511_s_stream(sd, false); adv7511_s_audio_stream(sd, false); + + if (state->i2c_cec == NULL) + return; + + v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk); + + /* cec soft reset */ + adv7511_cec_write(sd, 0x50, 0x01); + adv7511_cec_write(sd, 0x50, 0x00); + + /* legacy mode */ + adv7511_cec_write(sd, 0x4a, 0x00); + + if (cec_clk % 750000 != 0) + v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", + __func__, cec_clk); + + ratio = (cec_clk / 750000) - 1; + adv7511_cec_write(sd, 0x4e, ratio << 2); } static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1476,6 +1814,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * client->addr << 1); v4l2_i2c_subdev_init(sd, client, &adv7511_ops); + sd->internal_ops = &adv7511_int_ops; hdl = &state->hdl; v4l2_ctrl_handler_init(hdl, 10); @@ -1516,26 +1855,47 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * chip_id[0] = adv7511_rd(sd, 0xf5); chip_id[1] = adv7511_rd(sd, 0xf6); if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { - v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]); + v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], + chip_id[1]); err = -EIO; goto err_entity; } - state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1); + state->i2c_edid = i2c_new_dummy(client->adapter, + state->i2c_edid_addr >> 1); if (state->i2c_edid == NULL) { v4l2_err(sd, "failed to register edid i2c client\n"); err = -ENOMEM; goto err_entity; } + adv7511_wr(sd, 0xe1, state->i2c_cec_addr); + if (state->pdata.cec_clk < 3000000 || + state->pdata.cec_clk > 100000000) { + v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n", + __func__, state->pdata.cec_clk); + state->pdata.cec_clk = 0; + } + + if (state->pdata.cec_clk) { + state->i2c_cec = i2c_new_dummy(client->adapter, + state->i2c_cec_addr >> 1); + if (state->i2c_cec == NULL) { + v4l2_err(sd, "failed to register cec i2c client\n"); + goto err_unreg_edid; + } + adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ + } else { + adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ + } + state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); if (state->i2c_pktmem == NULL) { v4l2_err(sd, "failed to register pktmem i2c client\n"); err = -ENOMEM; - goto err_unreg_edid; + goto err_unreg_cec; } - adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ state->work_queue = create_singlethread_workqueue(sd->name); if (state->work_queue == NULL) { v4l2_err(sd, "could not create workqueue\n"); @@ -1546,6 +1906,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); adv7511_init_setup(sd); + +#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) + state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, + state, dev_name(&client->dev), CEC_CAP_TRANSMIT | + CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC, + ADV7511_MAX_ADDRS, &client->dev); + err = PTR_ERR_OR_ZERO(state->cec_adap); + if (err) { + destroy_workqueue(state->work_queue); + goto err_unreg_pktmem; + } +#endif + adv7511_set_isr(sd, true); adv7511_check_monitor_present_status(sd); @@ -1555,6 +1928,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * err_unreg_pktmem: i2c_unregister_device(state->i2c_pktmem); +err_unreg_cec: + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); err_unreg_edid: i2c_unregister_device(state->i2c_edid); err_entity: @@ -1576,9 +1952,12 @@ static int adv7511_remove(struct i2c_client *client) v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); + adv7511_set_isr(sd, false); adv7511_init_setup(sd); cancel_delayed_work(&state->edid_handler); i2c_unregister_device(state->i2c_edid); + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); i2c_unregister_device(state->i2c_pktmem); destroy_workqueue(state->work_queue); v4l2_device_unregister_subdev(sd); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 3f1ab4986cfc..4003831de712 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,8 @@ MODULE_LICENSE("GPL"); #define ADV76XX_OP_SWAP_CB_CR (1 << 0) +#define ADV76XX_MAX_ADDRS (3) + enum adv76xx_type { ADV7604, ADV7611, @@ -164,6 +167,7 @@ struct adv76xx_state { struct adv76xx_platform_data pdata; struct gpio_desc *hpd_gpio[4]; + struct gpio_desc *reset_gpio; struct v4l2_subdev sd; struct media_pad pads[ADV76XX_PAD_MAX]; @@ -184,10 +188,15 @@ struct adv76xx_state { u16 spa_port_a[2]; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; - struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; + /* CEC */ + struct cec_adapter *cec_adap; + u8 cec_addr[ADV76XX_MAX_ADDRS]; + u8 cec_valid_addrs; + bool cec_enabled_adap; + /* i2c clients */ struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX]; @@ -381,7 +390,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); } -static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, + u8 val) { return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); } @@ -414,6 +424,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); } +static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, + u8 val) +{ + return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); +} + static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); @@ -892,9 +908,9 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); const struct adv76xx_chip_info *info = state->info; + u16 cable_det = info->read_cable_det(sd); - return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, - info->read_cable_det(sd)); + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); } static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -1086,6 +1102,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) struct adv76xx_state *state = to_state(sd); bool rgb_output = io_read(sd, 0x02) & 0x02; bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; + u8 y = HDMI_COLORSPACE_RGB; + + if (hdmi_signal && (io_read(sd, 0x60) & 1)) + y = infoframe_read(sd, 0x01) >> 5; v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", __func__, state->rgb_quantization_range, @@ -1093,6 +1113,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0); adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0); + io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4); switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: @@ -1142,6 +1163,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; } + if (y != HDMI_COLORSPACE_RGB) + break; + /* RGB limited range (16-235) */ io_write_clr_set(sd, 0x02, 0xf0, 0x00); @@ -1153,6 +1177,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; } + if (y != HDMI_COLORSPACE_RGB) + break; + /* RGB full range (0-255) */ io_write_clr_set(sd, 0x02, 0xf0, 0x10); @@ -1849,6 +1876,7 @@ static void adv76xx_setup_format(struct adv76xx_state *state) io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state)); io_write_clr_set(sd, 0x05, 0x01, state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0); + set_rgb_quantization_range(sd); } static int adv76xx_get_format(struct v4l2_subdev *sd, @@ -1924,6 +1952,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, return 0; } +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) +static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) +{ + struct adv76xx_state *state = to_state(sd); + + if ((cec_read(sd, 0x11) & 0x01) == 0) { + v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); + return; + } + + if (tx_raw_status & 0x02) { + v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", + __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, + 1, 0, 0, 0); + } + if (tx_raw_status & 0x04) { + u8 status; + u8 nack_cnt; + u8 low_drive_cnt; + + v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); + /* + * We set this status bit since this hardware performs + * retransmissions. + */ + status = CEC_TX_STATUS_MAX_RETRIES; + nack_cnt = cec_read(sd, 0x14) & 0xf; + if (nack_cnt) + status |= CEC_TX_STATUS_NACK; + low_drive_cnt = cec_read(sd, 0x14) >> 4; + if (low_drive_cnt) + status |= CEC_TX_STATUS_LOW_DRIVE; + cec_transmit_done(state->cec_adap, status, + 0, nack_cnt, low_drive_cnt, 0); + return; + } + if (tx_raw_status & 0x01) { + v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); + return; + } +} + +static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) +{ + struct adv76xx_state *state = to_state(sd); + u8 cec_irq; + + /* cec controller */ + cec_irq = io_read(sd, 0x4d) & 0x0f; + if (!cec_irq) + return; + + v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); + adv76xx_cec_tx_raw_status(sd, cec_irq); + if (cec_irq & 0x08) { + struct cec_msg msg; + + msg.len = cec_read(sd, 0x25) & 0x1f; + if (msg.len > 16) + msg.len = 16; + + if (msg.len) { + u8 i; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = cec_read(sd, i + 0x15); + cec_write(sd, 0x26, 0x01); /* re-enable rx */ + cec_received_msg(state->cec_adap, &msg); + } + } + + /* note: the bit order is swapped between 0x4d and 0x4e */ + cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | + ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); + io_write(sd, 0x4e, cec_irq); + + if (handled) + *handled = true; +} + +static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct adv76xx_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + + if (!state->cec_enabled_adap && enable) { + cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */ + cec_write(sd, 0x2c, 0x01); /* cec soft reset */ + cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */ + /* enabled irqs: */ + /* tx: ready */ + /* tx: arbitration lost */ + /* tx: retry timeout */ + /* rx: ready */ + io_write_clr_set(sd, 0x50, 0x0f, 0x0f); + cec_write(sd, 0x26, 0x01); /* enable rx */ + } else if (state->cec_enabled_adap && !enable) { + /* disable cec interrupts */ + io_write_clr_set(sd, 0x50, 0x0f, 0x00); + /* disable address mask 1-3 */ + cec_write_clr_set(sd, 0x27, 0x70, 0x00); + /* power down cec section */ + cec_write_clr_set(sd, 0x2a, 0x01, 0x00); + state->cec_valid_addrs = 0; + } + state->cec_enabled_adap = enable; + adv76xx_s_detect_tx_5v_ctrl(sd); + return 0; +} + +static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +{ + struct adv76xx_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + unsigned int i, free_idx = ADV76XX_MAX_ADDRS; + + if (!state->cec_enabled_adap) + return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; + + if (addr == CEC_LOG_ADDR_INVALID) { + cec_write_clr_set(sd, 0x27, 0x70, 0); + state->cec_valid_addrs = 0; + return 0; + } + + for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (free_idx == ADV76XX_MAX_ADDRS && !is_valid) + free_idx = i; + if (is_valid && state->cec_addr[i] == addr) + return 0; + } + if (i == ADV76XX_MAX_ADDRS) { + i = free_idx; + if (i == ADV76XX_MAX_ADDRS) + return -ENXIO; + } + state->cec_addr[i] = addr; + state->cec_valid_addrs |= 1 << i; + + switch (i) { + case 0: + /* enable address mask 0 */ + cec_write_clr_set(sd, 0x27, 0x10, 0x10); + /* set address for mask 0 */ + cec_write_clr_set(sd, 0x28, 0x0f, addr); + break; + case 1: + /* enable address mask 1 */ + cec_write_clr_set(sd, 0x27, 0x20, 0x20); + /* set address for mask 1 */ + cec_write_clr_set(sd, 0x28, 0xf0, addr << 4); + break; + case 2: + /* enable address mask 2 */ + cec_write_clr_set(sd, 0x27, 0x40, 0x40); + /* set address for mask 1 */ + cec_write_clr_set(sd, 0x29, 0x0f, addr); + break; + } + return 0; +} + +static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct adv76xx_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + u8 len = msg->len; + unsigned int i; + + /* + * The number of retries is the number of attempts - 1, but retry + * at least once. It's not clear if a value of 0 is allowed, so + * let's do at least one retry. + */ + cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4); + + if (len > 16) { + v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); + return -EINVAL; + } + + /* write data */ + for (i = 0; i < len; i++) + cec_write(sd, i, msg->msg[i]); + + /* set length (data + header) */ + cec_write(sd, 0x10, len); + /* start transmit, enable tx */ + cec_write(sd, 0x11, 0x01); + return 0; +} + +static const struct cec_adap_ops adv76xx_cec_adap_ops = { + .adap_enable = adv76xx_cec_adap_enable, + .adap_log_addr = adv76xx_cec_adap_log_addr, + .adap_transmit = adv76xx_cec_adap_transmit, +}; +#endif + static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv76xx_state *state = to_state(sd); @@ -1969,6 +2201,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) *handled = true; } +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) + /* cec */ + adv76xx_cec_isr(sd, handled); +#endif + /* tx 5v detect */ tx_5v = irq_reg_0x70 & info->cable_det_mask; if (tx_5v) { @@ -2018,39 +2255,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return 0; } -static int get_edid_spa_location(const u8 *edid) -{ - u8 d; - - if ((edid[0x7e] != 1) || - (edid[0x80] != 0x02) || - (edid[0x81] != 0x03)) { - return -1; - } - - /* search Vendor Specific Data Block (tag 3) */ - d = edid[0x82] & 0x7f; - if (d > 4) { - int i = 0x84; - int end = 0x80 + d; - - do { - u8 tag = edid[i] >> 5; - u8 len = edid[i] & 0x1f; - - if ((tag == 3) && (len >= 5)) - return i + 4; - i += len + 1; - } while (i < end); - } - return -1; -} - static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct adv76xx_state *state = to_state(sd); const struct adv76xx_chip_info *info = state->info; - int spa_loc; + unsigned int spa_loc; + u16 pa; int err; int i; @@ -2081,6 +2291,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) edid->blocks = 2; return -E2BIG; } + pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); + err = cec_phys_addr_validate(pa, &pa, NULL); + if (err) + return err; v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); @@ -2090,9 +2304,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) adv76xx_set_hpd(state, 0); rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); - spa_loc = get_edid_spa_location(edid->edid); - if (spa_loc < 0) - spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ + /* + * Return an error if no location of the source physical address + * was found. + */ + if (spa_loc == 0) + return -EINVAL; switch (edid->pad) { case ADV76XX_PAD_HDMI_PORT_A: @@ -2152,10 +2369,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); return -EIO; } + cec_s_phys_addr(state->cec_adap, pa, false); /* enable hotplug after 100 ms */ - queue_delayed_work(state->work_queues, - &state->delayed_work_enable_hotplug, HZ / 10); + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); return 0; } @@ -2276,8 +2493,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) ((edid_enabled & 0x02) ? "Yes" : "No"), ((edid_enabled & 0x04) ? "Yes" : "No"), ((edid_enabled & 0x08) ? "Yes" : "No")); - v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? + v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? "enabled" : "disabled"); + if (state->cec_enabled_adap) { + int i; + + for (i = 0; i < ADV76XX_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (is_valid) + v4l2_info(sd, "CEC Logical Address: 0x%x\n", + state->cec_addr[i]); + } + } v4l2_info(sd, "-----Signal status-----\n"); cable_det = info->read_cable_det(sd); @@ -2323,11 +2551,10 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) rgb_quantization_range_txt[state->rgb_quantization_range]); v4l2_info(sd, "Input color space: %s\n", input_color_space_txt[reg_io_0x02 >> 4]); - v4l2_info(sd, "Output color space: %s %s, saturator %s, alt-gamma %s\n", + v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n", (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", - (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? - "enabled" : "disabled", + "(16-235)" : "(0-255)", (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]); @@ -2387,6 +2614,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd, } } +static int adv76xx_registered(struct v4l2_subdev *sd) +{ + struct adv76xx_state *state = to_state(sd); + int err; + + err = cec_register_adapter(state->cec_adap); + if (err) + cec_delete_adapter(state->cec_adap); + return err; +} + +static void adv76xx_unregistered(struct v4l2_subdev *sd) +{ + struct adv76xx_state *state = to_state(sd); + + cec_unregister_adapter(state->cec_adap); +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { @@ -2430,6 +2675,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = { .pad = &adv76xx_pad_ops, }; +static const struct v4l2_subdev_internal_ops adv76xx_int_ops = { + .registered = adv76xx_registered, + .unregistered = adv76xx_unregistered, +}; + /* -------------------------- custom ctrls ---------------------------------- */ static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { @@ -2492,10 +2742,7 @@ static int adv76xx_core_init(struct v4l2_subdev *sd) cp_write(sd, 0xcf, 0x01); /* Power down macrovision */ /* video format */ - io_write_clr_set(sd, 0x02, 0x0f, - pdata->alt_gamma << 3 | - pdata->op_656_range << 2 | - pdata->alt_data_sat << 0); + io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3); io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | pdata->insert_av_codes << 2 | pdata->replicate_av_codes << 1); @@ -2845,10 +3092,8 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) state->pdata.inv_llc_pol = 1; - if (bus_cfg.bus_type == V4L2_MBUS_BT656) { + if (bus_cfg.bus_type == V4L2_MBUS_BT656) state->pdata.insert_av_codes = 1; - state->pdata.op_656_range = 1; - } /* Disable the interrupt for now as no DT-based board uses it. */ state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED; @@ -2871,7 +3116,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) state->pdata.disable_pwrdnb = 0; state->pdata.disable_cable_det_rst = 0; state->pdata.blank_data = 1; - state->pdata.alt_data_sat = 1; state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; state->pdata.bus_order = ADV7604_BUS_ORDER_RGB; @@ -3020,6 +3264,19 @@ static int configure_regmaps(struct adv76xx_state *state) return 0; } +static void adv76xx_reset(struct adv76xx_state *state) +{ + if (state->reset_gpio) { + /* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */ + gpiod_set_value_cansleep(state->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(state->reset_gpio, 1); + /* It is recommended to wait 5 ms after the low pulse before */ + /* an I2C write is performed to the ADV76XX. */ + usleep_range(5000, 10000); + } +} + static int adv76xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -3083,6 +3340,12 @@ static int adv76xx_probe(struct i2c_client *client, if (state->hpd_gpio[i]) v4l_info(client, "Handling HPD %u GPIO\n", i); } + state->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(state->reset_gpio)) + return PTR_ERR(state->reset_gpio); + + adv76xx_reset(state); state->timings = cea640x480; state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); @@ -3093,6 +3356,7 @@ static int adv76xx_probe(struct i2c_client *client, id->name, i2c_adapter_id(client->adapter), client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->internal_ops = &adv76xx_int_ops; /* Configure IO Regmap region */ err = configure_regmap(state, ADV76XX_PAGE_IO); @@ -3206,14 +3470,6 @@ static int adv76xx_probe(struct i2c_client *client, } } - /* work queues */ - state->work_queues = create_singlethread_workqueue(client->name); - if (!state->work_queues) { - v4l2_err(sd, "Could not create work queue\n"); - err = -ENOMEM; - goto err_i2c; - } - INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv76xx_delayed_work_enable_hotplug); @@ -3236,6 +3492,18 @@ static int adv76xx_probe(struct i2c_client *client, err = adv76xx_core_init(sd); if (err) goto err_entity; + +#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) + state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops, + state, dev_name(&client->dev), + CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | + CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS, + &client->dev); + err = PTR_ERR_OR_ZERO(state->cec_adap); + if (err) + goto err_entity; +#endif + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); @@ -3249,7 +3517,6 @@ static int adv76xx_probe(struct i2c_client *client, media_entity_cleanup(&sd->entity); err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); err_i2c: adv76xx_unregister_clients(state); err_hdl: @@ -3264,8 +3531,14 @@ static int adv76xx_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct adv76xx_state *state = to_state(sd); + /* disable interrupts */ + io_write(sd, 0x40, 0); + io_write(sd, 0x41, 0); + io_write(sd, 0x46, 0); + io_write(sd, 0x6e, 0); + io_write(sd, 0x73, 0); + cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); adv76xx_unregister_clients(to_state(sd)); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ecaacb0a6fa1..8c2a52e280af 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,8 @@ MODULE_LICENSE("GPL"); #define ADV7842_OP_SWAP_CB_CR (1 << 0) +#define ADV7842_MAX_ADDRS (3) + /* ********************************************************************** * @@ -118,7 +121,6 @@ struct adv7842_state { struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; bool is_cea_format; - struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; bool hdmi_port_a; @@ -142,6 +144,11 @@ struct adv7842_state { struct v4l2_ctrl *free_run_color_ctrl_manual; struct v4l2_ctrl *free_run_color_ctrl; struct v4l2_ctrl *rgb_quantization_range_ctrl; + + struct cec_adapter *cec_adap; + u8 cec_addr[ADV7842_MAX_ADDRS]; + u8 cec_valid_addrs; + bool cec_enabled_adap; }; /* Unsupported timings. This device cannot support 720p30. */ @@ -418,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state->i2c_cec, reg, val); } -static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); + return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); } static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) @@ -696,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd) /* ----------------------------------------------------------------------- */ +static u16 adv7842_read_cable_det(struct v4l2_subdev *sd) +{ + u8 reg = io_read(sd, 0x6f); + u16 val = 0; + + if (reg & 0x02) + val |= 1; /* port A */ + if (reg & 0x01) + val |= 2; /* port B */ + return val; +} + static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -756,56 +775,23 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd) } /* enable hotplug after 200 ms */ - queue_delayed_work(state->work_queues, - &state->delayed_work_enable_hotplug, HZ / 5); + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5); return 0; } -static int edid_spa_location(const u8 *edid) -{ - u8 d; - - /* - * TODO, improve and update for other CEA extensions - * currently only for 1 segment (256 bytes), - * i.e. 1 extension block and CEA revision 3. - */ - if ((edid[0x7e] != 1) || - (edid[0x80] != 0x02) || - (edid[0x81] != 0x03)) { - return -EINVAL; - } - /* - * search Vendor Specific Data Block (tag 3) - */ - d = edid[0x82] & 0x7f; - if (d > 4) { - int i = 0x84; - int end = 0x80 + d; - do { - u8 tag = edid[i]>>5; - u8 len = edid[i] & 0x1f; - - if ((tag == 3) && (len >= 5)) - return i + 4; - i += len + 1; - } while (i < end); - } - return -EINVAL; -} - static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7842_state *state = to_state(sd); - const u8 *val = state->hdmi_edid.edid; - int spa_loc = edid_spa_location(val); + const u8 *edid = state->hdmi_edid.edid; + int spa_loc; + u16 pa; int err = 0; int i; - v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", - __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc); + v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n", + __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); /* HPA disable on port A and B */ io_write_and_or(sd, 0x20, 0xcf, 0x00); @@ -816,24 +802,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) if (!state->hdmi_edid.present) return 0; + pa = cec_get_edid_phys_addr(edid, 256, &spa_loc); + err = cec_phys_addr_validate(pa, &pa, NULL); + if (err) + return err; + + /* + * Return an error if no location of the source physical address + * was found. + */ + if (spa_loc == 0) + return -EINVAL; + /* edid segment pointer '0' for HDMI ports */ rep_write_and_or(sd, 0x77, 0xef, 0x00); for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, - I2C_SMBUS_BLOCK_MAX, val + i); + I2C_SMBUS_BLOCK_MAX, edid + i); if (err) return err; - if (spa_loc < 0) - spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ - if (port == ADV7842_EDID_PORT_A) { - rep_write(sd, 0x72, val[spa_loc]); - rep_write(sd, 0x73, val[spa_loc + 1]); + rep_write(sd, 0x72, edid[spa_loc]); + rep_write(sd, 0x73, edid[spa_loc + 1]); } else { - rep_write(sd, 0x74, val[spa_loc]); - rep_write(sd, 0x75, val[spa_loc + 1]); + rep_write(sd, 0x74, edid[spa_loc]); + rep_write(sd, 0x75, edid[spa_loc + 1]); } rep_write(sd, 0x76, spa_loc & 0xff); rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); @@ -853,10 +848,10 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); return -EIO; } + cec_s_phys_addr(state->cec_adap, pa, false); /* enable hotplug after 200 ms */ - queue_delayed_work(state->work_queues, - &state->delayed_work_enable_hotplug, HZ / 5); + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5); return 0; } @@ -983,20 +978,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd, static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); - int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl); - u8 reg_io_6f = io_read(sd, 0x6f); - int val = 0; + u16 cable_det = adv7842_read_cable_det(sd); - if (reg_io_6f & 0x02) - val |= 1; /* port A */ - if (reg_io_6f & 0x01) - val |= 2; /* port B */ - - v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val); + v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det); - if (val != prev) - return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val); - return 0; + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det); } static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -1198,6 +1184,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) struct adv7842_state *state = to_state(sd); bool rgb_output = io_read(sd, 0x02) & 0x02; bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; + u8 y = HDMI_COLORSPACE_RGB; + + if (hdmi_signal && (io_read(sd, 0x60) & 1)) + y = infoframe_read(sd, 0x01) >> 5; v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", __func__, state->rgb_quantization_range, @@ -1205,6 +1195,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) adv7842_set_gain(sd, true, 0x0, 0x0, 0x0); adv7842_set_offset(sd, true, 0x0, 0x0, 0x0); + io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4); switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: @@ -1254,6 +1245,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; } + if (y != HDMI_COLORSPACE_RGB) + break; + /* RGB limited range (16-235) */ io_write_and_or(sd, 0x02, 0x0f, 0x00); @@ -1265,6 +1259,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; } + if (y != HDMI_COLORSPACE_RGB) + break; + /* RGB full range (0-255) */ io_write_and_or(sd, 0x02, 0x0f, 0x10); @@ -2072,6 +2069,7 @@ static void adv7842_setup_format(struct adv7842_state *state) io_write_clr_set(sd, 0x04, 0xe0, adv7842_op_ch_sel(state)); io_write_clr_set(sd, 0x05, 0x01, state->format->swap_cb_cr ? ADV7842_OP_SWAP_CB_CR : 0); + set_rgb_quantization_range(sd); } static int adv7842_get_format(struct v4l2_subdev *sd, @@ -2170,6 +2168,207 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable) } } +#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) +static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) +{ + struct adv7842_state *state = to_state(sd); + + if ((cec_read(sd, 0x11) & 0x01) == 0) { + v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); + return; + } + + if (tx_raw_status & 0x02) { + v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", + __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST, + 1, 0, 0, 0); + return; + } + if (tx_raw_status & 0x04) { + u8 status; + u8 nack_cnt; + u8 low_drive_cnt; + + v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); + /* + * We set this status bit since this hardware performs + * retransmissions. + */ + status = CEC_TX_STATUS_MAX_RETRIES; + nack_cnt = cec_read(sd, 0x14) & 0xf; + if (nack_cnt) + status |= CEC_TX_STATUS_NACK; + low_drive_cnt = cec_read(sd, 0x14) >> 4; + if (low_drive_cnt) + status |= CEC_TX_STATUS_LOW_DRIVE; + cec_transmit_done(state->cec_adap, status, + 0, nack_cnt, low_drive_cnt, 0); + return; + } + if (tx_raw_status & 0x01) { + v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); + cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); + return; + } +} + +static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled) +{ + u8 cec_irq; + + /* cec controller */ + cec_irq = io_read(sd, 0x93) & 0x0f; + if (!cec_irq) + return; + + v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); + adv7842_cec_tx_raw_status(sd, cec_irq); + if (cec_irq & 0x08) { + struct adv7842_state *state = to_state(sd); + struct cec_msg msg; + + msg.len = cec_read(sd, 0x25) & 0x1f; + if (msg.len > 16) + msg.len = 16; + + if (msg.len) { + u8 i; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = cec_read(sd, i + 0x15); + cec_write(sd, 0x26, 0x01); /* re-enable rx */ + cec_received_msg(state->cec_adap, &msg); + } + } + + io_write(sd, 0x94, cec_irq); + + if (handled) + *handled = true; +} + +static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct adv7842_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + + if (!state->cec_enabled_adap && enable) { + cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */ + cec_write(sd, 0x2c, 0x01); /* cec soft reset */ + cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */ + /* enabled irqs: */ + /* tx: ready */ + /* tx: arbitration lost */ + /* tx: retry timeout */ + /* rx: ready */ + io_write_clr_set(sd, 0x96, 0x0f, 0x0f); + cec_write(sd, 0x26, 0x01); /* enable rx */ + } else if (state->cec_enabled_adap && !enable) { + /* disable cec interrupts */ + io_write_clr_set(sd, 0x96, 0x0f, 0x00); + /* disable address mask 1-3 */ + cec_write_clr_set(sd, 0x27, 0x70, 0x00); + /* power down cec section */ + cec_write_clr_set(sd, 0x2a, 0x01, 0x00); + state->cec_valid_addrs = 0; + } + state->cec_enabled_adap = enable; + return 0; +} + +static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +{ + struct adv7842_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + unsigned int i, free_idx = ADV7842_MAX_ADDRS; + + if (!state->cec_enabled_adap) + return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; + + if (addr == CEC_LOG_ADDR_INVALID) { + cec_write_clr_set(sd, 0x27, 0x70, 0); + state->cec_valid_addrs = 0; + return 0; + } + + for (i = 0; i < ADV7842_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (free_idx == ADV7842_MAX_ADDRS && !is_valid) + free_idx = i; + if (is_valid && state->cec_addr[i] == addr) + return 0; + } + if (i == ADV7842_MAX_ADDRS) { + i = free_idx; + if (i == ADV7842_MAX_ADDRS) + return -ENXIO; + } + state->cec_addr[i] = addr; + state->cec_valid_addrs |= 1 << i; + + switch (i) { + case 0: + /* enable address mask 0 */ + cec_write_clr_set(sd, 0x27, 0x10, 0x10); + /* set address for mask 0 */ + cec_write_clr_set(sd, 0x28, 0x0f, addr); + break; + case 1: + /* enable address mask 1 */ + cec_write_clr_set(sd, 0x27, 0x20, 0x20); + /* set address for mask 1 */ + cec_write_clr_set(sd, 0x28, 0xf0, addr << 4); + break; + case 2: + /* enable address mask 2 */ + cec_write_clr_set(sd, 0x27, 0x40, 0x40); + /* set address for mask 1 */ + cec_write_clr_set(sd, 0x29, 0x0f, addr); + break; + } + return 0; +} + +static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct adv7842_state *state = adap->priv; + struct v4l2_subdev *sd = &state->sd; + u8 len = msg->len; + unsigned int i; + + /* + * The number of retries is the number of attempts - 1, but retry + * at least once. It's not clear if a value of 0 is allowed, so + * let's do at least one retry. + */ + cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4); + + if (len > 16) { + v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); + return -EINVAL; + } + + /* write data */ + for (i = 0; i < len; i++) + cec_write(sd, i, msg->msg[i]); + + /* set length (data + header) */ + cec_write(sd, 0x10, len); + /* start transmit, enable tx */ + cec_write(sd, 0x11, 0x01); + return 0; +} + +static const struct cec_adap_ops adv7842_cec_adap_ops = { + .adap_enable = adv7842_cec_adap_enable, + .adap_log_addr = adv7842_cec_adap_log_addr, + .adap_transmit = adv7842_cec_adap_transmit, +}; +#endif + static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7842_state *state = to_state(sd); @@ -2241,6 +2440,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) *handled = true; } +#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) + /* cec */ + adv7842_cec_isr(sd, handled); +#endif + /* tx 5v detect */ if (irq_status[2] & 0x3) { v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__); @@ -2321,10 +2525,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e) case ADV7842_EDID_PORT_A: case ADV7842_EDID_PORT_B: memset(&state->hdmi_edid.edid, 0, 256); - if (e->blocks) + if (e->blocks) { state->hdmi_edid.present |= 0x04 << e->pad; - else + } else { state->hdmi_edid.present &= ~(0x04 << e->pad); + adv7842_s_detect_tx_5v_ctrl(sd); + } memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); err = edid_write_hdmi_segment(sd, e->pad); break; @@ -2397,6 +2603,8 @@ static void adv7842_log_infoframes(struct v4l2_subdev *sd) log_infoframe(sd, &cri[i]); } +#if 0 +/* Let's keep it here for now, as it could be useful for debug */ static const char * const prim_mode_txt[] = { "SDP", "Component", @@ -2415,6 +2623,7 @@ static const char * const prim_mode_txt[] = { "Reserved", "Reserved", }; +#endif static int adv7842_sdp_log_status(struct v4l2_subdev *sd) { @@ -2509,8 +2718,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "HPD A %s, B %s\n", reg_io_0x21 & 0x02 ? "enabled" : "disabled", reg_io_0x21 & 0x01 ? "enabled" : "disabled"); - v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? + v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ? "enabled" : "disabled"); + if (state->cec_enabled_adap) { + int i; + + for (i = 0; i < ADV7842_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (is_valid) + v4l2_info(sd, "CEC Logical Address: 0x%x\n", + state->cec_addr[i]); + } + } v4l2_info(sd, "-----Signal status-----\n"); if (state->hdmi_port_a) { @@ -2569,11 +2789,11 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) rgb_quantization_range_txt[state->rgb_quantization_range]); v4l2_info(sd, "Input color space: %s\n", input_color_space_txt[reg_io_0x02 >> 4]); - v4l2_info(sd, "Output color space: %s %s, saturator %s\n", + v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n", (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", - (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", - ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? - "enabled" : "disabled"); + (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? + "(16-235)" : "(0-255)", + (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]); @@ -2777,11 +2997,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd) io_write(sd, 0x15, 0x80); /* Power up pads */ /* video format */ - io_write(sd, 0x02, - 0xf0 | - pdata->alt_gamma << 3 | - pdata->op_656_range << 2 | - pdata->alt_data_sat << 0); + io_write(sd, 0x02, 0xf0 | pdata->alt_gamma << 3); io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | pdata->insert_av_codes << 2 | pdata->replicate_av_codes << 1); @@ -3031,6 +3247,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd, } } +static int adv7842_registered(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + int err; + + err = cec_register_adapter(state->cec_adap); + if (err) + cec_delete_adapter(state->cec_adap); + return err; +} + +static void adv7842_unregistered(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + cec_unregister_adapter(state->cec_adap); +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { @@ -3077,6 +3311,11 @@ static const struct v4l2_subdev_ops adv7842_ops = { .pad = &adv7842_pad_ops, }; +static const struct v4l2_subdev_internal_ops adv7842_int_ops = { + .registered = adv7842_registered, + .unregistered = adv7842_unregistered, +}; + /* -------------------------- custom ctrls ---------------------------------- */ static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = { @@ -3241,6 +3480,7 @@ static int adv7842_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->internal_ops = &adv7842_int_ops; state->mode = pdata->mode; state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; @@ -3311,13 +3551,6 @@ static int adv7842_probe(struct i2c_client *client, goto err_i2c; } - /* work queues */ - state->work_queues = create_singlethread_workqueue(client->name); - if (!state->work_queues) { - v4l2_err(sd, "Could not create work queue\n"); - err = -ENOMEM; - goto err_i2c; - } INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv7842_delayed_work_enable_hotplug); @@ -3331,6 +3564,17 @@ static int adv7842_probe(struct i2c_client *client, if (err) goto err_entity; +#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC) + state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops, + state, dev_name(&client->dev), + CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | + CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS, + &client->dev); + err = PTR_ERR_OR_ZERO(state->cec_adap); + if (err) + goto err_entity; +#endif + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); return 0; @@ -3339,7 +3583,6 @@ static int adv7842_probe(struct i2c_client *client, media_entity_cleanup(&sd->entity); err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); err_i2c: adv7842_unregister_clients(sd); err_hdl: @@ -3355,9 +3598,7 @@ static int adv7842_remove(struct i2c_client *client) struct adv7842_state *state = to_state(sd); adv7842_irq_enable(sd, false); - cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); adv7842_unregister_clients(sd); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index b7e87e38642a..e4b3cf49dd38 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -121,13 +121,6 @@ static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { .log_status = cs53l32a_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 07a3e7173144..142ae28803bb 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5042,13 +5042,6 @@ static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { static const struct v4l2_subdev_core_ops cx25840_core_ops = { .log_status = cx25840_log_status, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .reset = cx25840_reset, .load_fw = cx25840_load_fw, .s_io_pin_config = common_s_io_pin_config, diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index e016626ebf89..503b7c4f0a9b 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -642,13 +642,6 @@ static const struct v4l2_ctrl_ops msp_ctrl_ops = { static const struct v4l2_subdev_core_ops msp_core_ops = { .log_status = msp_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_video_ops msp_video_ops = { diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 702d562f8e39..842017fa4aab 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -233,10 +233,21 @@ static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) ret = mt9t001_reset(mt9t001); if (ret < 0) { dev_err(&client->dev, "Failed to reset the camera\n"); - return ret; + goto e_power; } - return v4l2_ctrl_handler_setup(&mt9t001->ctrls); + ret = v4l2_ctrl_handler_setup(&mt9t001->ctrls); + if (ret < 0) { + dev_err(&client->dev, "Failed to set up control handlers\n"); + goto e_power; + } + + return 0; + +e_power: + mt9t001_power_off(mt9t001); + + return ret; } /* ----------------------------------------------------------------------------- @@ -834,7 +845,7 @@ static struct v4l2_subdev_ops mt9t001_subdev_ops = { .pad = &mt9t001_subdev_pad_ops, }; -static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { +static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { .registered = mt9t001_registered, .open = mt9t001_open, .close = mt9t001_close, diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 501b37039449..58eb62f1ba21 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -133,9 +132,16 @@ #define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) #define MT9V032_TEST_PATTERN_ENABLE (1 << 13) #define MT9V032_TEST_PATTERN_FLIP (1 << 14) +#define MT9V032_AEGC_DESIRED_BIN 0xa5 +#define MT9V032_AEC_UPDATE_FREQUENCY 0xa6 +#define MT9V032_AEC_LPF 0xa8 +#define MT9V032_AGC_UPDATE_FREQUENCY 0xa9 +#define MT9V032_AGC_LPF 0xaa #define MT9V032_AEC_AGC_ENABLE 0xaf #define MT9V032_AEC_ENABLE (1 << 0) #define MT9V032_AGC_ENABLE (1 << 1) +#define MT9V034_AEC_MAX_SHUTTER_WIDTH 0xad +#define MT9V032_AEC_MAX_SHUTTER_WIDTH 0xbd #define MT9V032_THERMAL_INFO 0xc1 enum mt9v032_model { @@ -162,6 +168,8 @@ struct mt9v032_model_data { unsigned int min_shutter; unsigned int max_shutter; unsigned int pclk_reg; + unsigned int aec_max_shutter_reg; + const struct v4l2_ctrl_config * const aec_max_shutter_v4l2_ctrl; }; struct mt9v032_model_info { @@ -175,63 +183,6 @@ static const struct mt9v032_model_version mt9v032_versions[] = { { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" }, }; -static const struct mt9v032_model_data mt9v032_model_data[] = { - { - /* MT9V022, MT9V032 revisions 1/2/3 */ - .min_row_time = 660, - .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, - .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, - .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, - .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, - .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, - .pclk_reg = MT9V032_PIXEL_CLOCK, - }, { - /* MT9V024, MT9V034 */ - .min_row_time = 690, - .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, - .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, - .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, - .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, - .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, - .pclk_reg = MT9V034_PIXEL_CLOCK, - }, -}; - -static const struct mt9v032_model_info mt9v032_models[] = { - [MT9V032_MODEL_V022_COLOR] = { - .data = &mt9v032_model_data[0], - .color = true, - }, - [MT9V032_MODEL_V022_MONO] = { - .data = &mt9v032_model_data[0], - .color = false, - }, - [MT9V032_MODEL_V024_COLOR] = { - .data = &mt9v032_model_data[1], - .color = true, - }, - [MT9V032_MODEL_V024_MONO] = { - .data = &mt9v032_model_data[1], - .color = false, - }, - [MT9V032_MODEL_V032_COLOR] = { - .data = &mt9v032_model_data[0], - .color = true, - }, - [MT9V032_MODEL_V032_MONO] = { - .data = &mt9v032_model_data[0], - .color = false, - }, - [MT9V032_MODEL_V034_COLOR] = { - .data = &mt9v032_model_data[1], - .color = true, - }, - [MT9V032_MODEL_V034_MONO] = { - .data = &mt9v032_model_data[1], - .color = false, - }, -}; - struct mt9v032 { struct v4l2_subdev subdev; struct media_pad pad; @@ -349,7 +300,8 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) if (ret < 0) return ret; - return regmap_write(map, MT9V032_CHIP_CONTROL, 0); + return regmap_write(map, MT9V032_CHIP_CONTROL, + MT9V032_CHIP_CONTROL_MASTER_MODE); } static void mt9v032_power_off(struct mt9v032 *mt9v032) @@ -421,8 +373,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *c static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) { - const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE - | MT9V032_CHIP_CONTROL_DOUT_ENABLE + const u16 mode = MT9V032_CHIP_CONTROL_DOUT_ENABLE | MT9V032_CHIP_CONTROL_SEQUENTIAL; struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_rect *crop = &mt9v032->crop; @@ -647,6 +598,34 @@ static int mt9v032_set_selection(struct v4l2_subdev *subdev, */ #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) +/* + * Value between 1 and 64 to set the desired bin. This is effectively a measure + * of how bright the image is supposed to be. Both AGC and AEC try to reach + * this. + */ +#define V4L2_CID_AEGC_DESIRED_BIN (V4L2_CID_USER_BASE | 0x1002) +/* + * LPF is the low pass filter capability of the chip. Both AEC and AGC have + * this setting. This limits the speed in which AGC/AEC adjust their settings. + * Possible values are 0-2. 0 means no LPF. For 1 and 2 this equation is used: + * + * if |(calculated new exp - current exp)| > (current exp / 4) + * next exp = calculated new exp + * else + * next exp = current exp + ((calculated new exp - current exp) / 2^LPF) + */ +#define V4L2_CID_AEC_LPF (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_AGC_LPF (V4L2_CID_USER_BASE | 0x1004) +/* + * Value between 0 and 15. This is the number of frames being skipped before + * updating the auto exposure/gain. + */ +#define V4L2_CID_AEC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1005) +#define V4L2_CID_AGC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1006) +/* + * Maximum shutter width used for AEC. + */ +#define V4L2_CID_AEC_MAX_SHUTTER_WIDTH (V4L2_CID_USER_BASE | 0x1007) static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) { @@ -716,6 +695,28 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) break; } return regmap_write(map, MT9V032_TEST_PATTERN, data); + + case V4L2_CID_AEGC_DESIRED_BIN: + return regmap_write(map, MT9V032_AEGC_DESIRED_BIN, ctrl->val); + + case V4L2_CID_AEC_LPF: + return regmap_write(map, MT9V032_AEC_LPF, ctrl->val); + + case V4L2_CID_AGC_LPF: + return regmap_write(map, MT9V032_AGC_LPF, ctrl->val); + + case V4L2_CID_AEC_UPDATE_INTERVAL: + return regmap_write(map, MT9V032_AEC_UPDATE_FREQUENCY, + ctrl->val); + + case V4L2_CID_AGC_UPDATE_INTERVAL: + return regmap_write(map, MT9V032_AGC_UPDATE_FREQUENCY, + ctrl->val); + + case V4L2_CID_AEC_MAX_SHUTTER_WIDTH: + return regmap_write(map, + mt9v032->model->data->aec_max_shutter_reg, + ctrl->val); } return 0; @@ -745,6 +746,84 @@ static const struct v4l2_ctrl_config mt9v032_test_pattern_color = { .flags = 0, }; +static const struct v4l2_ctrl_config mt9v032_aegc_controls[] = { + { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AEGC_DESIRED_BIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AEC/AGC Desired Bin", + .min = 1, + .max = 64, + .step = 1, + .def = 58, + .flags = 0, + }, { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AEC_LPF, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AEC Low Pass Filter", + .min = 0, + .max = 2, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AGC_LPF, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AGC Low Pass Filter", + .min = 0, + .max = 2, + .step = 1, + .def = 2, + .flags = 0, + }, { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AEC_UPDATE_INTERVAL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AEC Update Interval", + .min = 0, + .max = 16, + .step = 1, + .def = 2, + .flags = 0, + }, { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AGC_UPDATE_INTERVAL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AGC Update Interval", + .min = 0, + .max = 16, + .step = 1, + .def = 2, + .flags = 0, + } +}; + +static const struct v4l2_ctrl_config mt9v032_aec_max_shutter_width = { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AEC Max Shutter Width", + .min = 1, + .max = 2047, + .step = 1, + .def = 480, + .flags = 0, +}; + +static const struct v4l2_ctrl_config mt9v034_aec_max_shutter_width = { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "AEC Max Shutter Width", + .min = 1, + .max = 32765, + .step = 1, + .def = 480, + .flags = 0, +}; + /* ----------------------------------------------------------------------------- * V4L2 subdev core operations */ @@ -953,13 +1032,6 @@ static int mt9v032_probe(struct i2c_client *client, unsigned int i; int ret; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL); if (!mt9v032) return -ENOMEM; @@ -986,7 +1058,8 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->pdata = pdata; mt9v032->model = (const void *)did->driver_data; - v4l2_ctrl_handler_init(&mt9v032->ctrls, 10); + v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 + + ARRAY_SIZE(mt9v032_aegc_controls)); v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); @@ -1015,6 +1088,13 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_test_pattern_color, NULL); + v4l2_ctrl_new_custom(&mt9v032->ctrls, + mt9v032->model->data->aec_max_shutter_v4l2_ctrl, + NULL); + for (i = 0; i < ARRAY_SIZE(mt9v032_aegc_controls); ++i) + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_aegc_controls[i], + NULL); + v4l2_ctrl_cluster(2, &mt9v032->test_pattern); mt9v032->pixel_rate = @@ -1103,6 +1183,67 @@ static int mt9v032_remove(struct i2c_client *client) return 0; } +static const struct mt9v032_model_data mt9v032_model_data[] = { + { + /* MT9V022, MT9V032 revisions 1/2/3 */ + .min_row_time = 660, + .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, + .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, + .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, + .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, + .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, + .pclk_reg = MT9V032_PIXEL_CLOCK, + .aec_max_shutter_reg = MT9V032_AEC_MAX_SHUTTER_WIDTH, + .aec_max_shutter_v4l2_ctrl = &mt9v032_aec_max_shutter_width, + }, { + /* MT9V024, MT9V034 */ + .min_row_time = 690, + .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, + .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, + .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, + .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, + .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, + .pclk_reg = MT9V034_PIXEL_CLOCK, + .aec_max_shutter_reg = MT9V034_AEC_MAX_SHUTTER_WIDTH, + .aec_max_shutter_v4l2_ctrl = &mt9v034_aec_max_shutter_width, + }, +}; + +static const struct mt9v032_model_info mt9v032_models[] = { + [MT9V032_MODEL_V022_COLOR] = { + .data = &mt9v032_model_data[0], + .color = true, + }, + [MT9V032_MODEL_V022_MONO] = { + .data = &mt9v032_model_data[0], + .color = false, + }, + [MT9V032_MODEL_V024_COLOR] = { + .data = &mt9v032_model_data[1], + .color = true, + }, + [MT9V032_MODEL_V024_MONO] = { + .data = &mt9v032_model_data[1], + .color = false, + }, + [MT9V032_MODEL_V032_COLOR] = { + .data = &mt9v032_model_data[0], + .color = true, + }, + [MT9V032_MODEL_V032_MONO] = { + .data = &mt9v032_model_data[0], + .color = false, + }, + [MT9V032_MODEL_V034_COLOR] = { + .data = &mt9v032_model_data[1], + .color = true, + }, + [MT9V032_MODEL_V034_MONO] = { + .data = &mt9v032_model_data[1], + .color = false, + }, +}; + static const struct i2c_device_id mt9v032_id[] = { { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] }, { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] }, diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index bd3526bdd539..58062b41c923 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1585,13 +1585,6 @@ static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { static const struct v4l2_subdev_core_ops saa711x_core_ops = { .log_status = saa711x_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio, #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 3dfe387abf6e..d08ab6c8357c 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -3044,10 +3044,8 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) pdata->op_sys_clock = devm_kcalloc( dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */, sizeof(*pdata->op_sys_clock), GFP_KERNEL); - if (!pdata->op_sys_clock) { - rval = -ENOMEM; + if (!pdata->op_sys_clock) goto out_err; - } for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) { pdata->op_sys_clock[i] = bus_cfg->link_frequencies[i]; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 6cf6d06737a5..1e3a0dd2238c 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -89,8 +89,6 @@ struct tc358743_state { struct v4l2_ctrl *audio_sampling_rate_ctrl; struct v4l2_ctrl *audio_present_ctrl; - /* work queues */ - struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; /* edid */ @@ -425,8 +423,7 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd) /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when * hotplug is enabled. See register DDC_CTL */ - queue_delayed_work(state->work_queues, - &state->delayed_work_enable_hotplug, HZ / 10); + schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); tc358743_enable_interrupts(sd, true); tc358743_s_ctrl_detect_tx_5v(sd); @@ -1884,14 +1881,6 @@ static int tc358743_probe(struct i2c_client *client, goto err_hdl; } - /* work queues */ - state->work_queues = create_singlethread_workqueue(client->name); - if (!state->work_queues) { - v4l2_err(sd, "Could not create work queue\n"); - err = -ENOMEM; - goto err_hdl; - } - state->pad.flags = MEDIA_PAD_FL_SOURCE; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err < 0) @@ -1940,7 +1929,6 @@ static int tc358743_probe(struct i2c_client *client, err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); mutex_destroy(&state->confctl_mutex); err_hdl: media_entity_cleanup(&sd->entity); @@ -1954,7 +1942,6 @@ static int tc358743_remove(struct i2c_client *client) struct tc358743_state *state = to_state(sd); cancel_delayed_work(&state->delayed_work_enable_hotplug); - destroy_workqueue(state->work_queues); v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); mutex_destroy(&state->confctl_mutex); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index fece2a4339a1..42d1e26e581c 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1855,13 +1855,6 @@ static const struct v4l2_ctrl_ops tvaudio_ctrl_ops = { static const struct v4l2_subdev_core_ops tvaudio_core_ops = { .log_status = tvaudio_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 6e00f145b948..5581f4db02af 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -178,13 +178,6 @@ static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { static const struct v4l2_subdev_core_ops wm8775_core_ops = { .log_status = wm8775_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index a1cd50f331f1..1795abeda658 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -423,7 +423,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; mutex_lock(&dev->graph_mutex); @@ -495,7 +495,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; switch (cmd) { @@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = { static ssize_t show_model(struct device *cd, struct device_attribute *attr, char *buf) { - struct media_device *mdev = to_media_device(to_media_devnode(cd)); + struct media_devnode *devnode = to_media_devnode(cd); + struct media_device *mdev = devnode->media_dev; return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); } @@ -704,23 +705,35 @@ EXPORT_SYMBOL_GPL(media_device_cleanup); int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { + struct media_devnode *devnode; int ret; + devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); + if (!devnode) + return -ENOMEM; + /* Register the device node. */ - mdev->devnode.fops = &media_device_fops; - mdev->devnode.parent = mdev->dev; - mdev->devnode.release = media_device_release; + mdev->devnode = devnode; + devnode->fops = &media_device_fops; + devnode->parent = mdev->dev; + devnode->release = media_device_release; /* Set version 0 to indicate user-space that the graph is static */ mdev->topology_version = 0; - ret = media_devnode_register(&mdev->devnode, owner); - if (ret < 0) + ret = media_devnode_register(mdev, devnode, owner); + if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; return ret; + } - ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); + ret = device_create_file(&devnode->dev, &dev_attr_model); if (ret < 0) { - media_devnode_unregister(&mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; + media_devnode_unregister_prepare(devnode); + media_devnode_unregister(devnode); return ret; } @@ -771,11 +784,14 @@ void media_device_unregister(struct media_device *mdev) mutex_lock(&mdev->graph_mutex); /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(&mdev->devnode)) { + if (!media_devnode_is_registered(mdev->devnode)) { mutex_unlock(&mdev->graph_mutex); return; } + /* Clear the devnode register bit to avoid races with media dev open */ + media_devnode_unregister_prepare(mdev->devnode); + /* Remove all entities from the media device */ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) __media_device_unregister_entity(entity); @@ -794,9 +810,12 @@ void media_device_unregister(struct media_device *mdev) mutex_unlock(&mdev->graph_mutex); - device_remove_file(&mdev->devnode.dev, &dev_attr_model); - dev_dbg(mdev->dev, "Media device unregistering\n"); - media_devnode_unregister(&mdev->devnode); + dev_dbg(mdev->dev, "Media device unregistered\n"); + + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; } EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index b66dc9d0766b..f2772ba6f611 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -44,6 +44,7 @@ #include #include +#include #define MEDIA_NUM_DEVICES 256 #define MEDIA_NAME "media" @@ -59,21 +60,19 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); /* Called when the last user of the media device exits. */ static void media_devnode_release(struct device *cd) { - struct media_devnode *mdev = to_media_devnode(cd); + struct media_devnode *devnode = to_media_devnode(cd); mutex_lock(&media_devnode_lock); - - /* Delete the cdev on this minor as well */ - cdev_del(&mdev->cdev); - /* Mark device node number as free */ - clear_bit(mdev->minor, media_devnode_nums); - + clear_bit(devnode->minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); /* Release media_devnode and perform other cleanups as needed. */ - if (mdev->release) - mdev->release(mdev); + if (devnode->release) + devnode->release(devnode); + + kfree(devnode); + pr_debug("%s: Media Devnode Deallocated\n", __func__); } static struct bus_type media_bus_type = { @@ -83,37 +82,37 @@ static struct bus_type media_bus_type = { static ssize_t media_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->read) + if (!devnode->fops->read) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->read(filp, buf, sz, off); + return devnode->fops->read(filp, buf, sz, off); } static ssize_t media_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->write) + if (!devnode->fops->write) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->write(filp, buf, sz, off); + return devnode->fops->write(filp, buf, sz, off); } static unsigned int media_poll(struct file *filp, struct poll_table_struct *poll) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return POLLERR | POLLHUP; - if (!mdev->fops->poll) + if (!devnode->fops->poll) return DEFAULT_POLLMASK; - return mdev->fops->poll(filp, poll); + return devnode->fops->poll(filp, poll); } static long @@ -121,12 +120,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, long (*ioctl_func)(struct file *filp, unsigned int cmd, unsigned long arg)) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); if (!ioctl_func) return -ENOTTY; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; return ioctl_func(filp, cmd, arg); @@ -134,9 +133,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); } #ifdef CONFIG_COMPAT @@ -144,9 +143,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static long media_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); } #endif /* CONFIG_COMPAT */ @@ -154,7 +153,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd, /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { - struct media_devnode *mdev; + struct media_devnode *devnode; int ret; /* Check if the media device is available. This needs to be done with @@ -164,23 +163,23 @@ static int media_open(struct inode *inode, struct file *filp) * a crash. */ mutex_lock(&media_devnode_lock); - mdev = container_of(inode->i_cdev, struct media_devnode, cdev); + devnode = container_of(inode->i_cdev, struct media_devnode, cdev); /* return ENXIO if the media device has been removed already or if it is not registered anymore. */ - if (!media_devnode_is_registered(mdev)) { + if (!media_devnode_is_registered(devnode)) { mutex_unlock(&media_devnode_lock); return -ENXIO; } /* and increase the device refcount */ - get_device(&mdev->dev); + get_device(&devnode->dev); mutex_unlock(&media_devnode_lock); - filp->private_data = mdev; + filp->private_data = devnode; - if (mdev->fops->open) { - ret = mdev->fops->open(filp); + if (devnode->fops->open) { + ret = devnode->fops->open(filp); if (ret) { - put_device(&mdev->dev); + put_device(&devnode->dev); filp->private_data = NULL; return ret; } @@ -192,16 +191,18 @@ static int media_open(struct inode *inode, struct file *filp) /* Override for the release function */ static int media_release(struct inode *inode, struct file *filp) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (mdev->fops->release) - mdev->fops->release(filp); + if (devnode->fops->release) + devnode->fops->release(filp); filp->private_data = NULL; /* decrease the refcount unconditionally since the release() return value is ignored. */ - put_device(&mdev->dev); + put_device(&devnode->dev); + + pr_debug("%s: Media Release\n", __func__); return 0; } @@ -219,7 +220,8 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -int __must_check media_devnode_register(struct media_devnode *mdev, +int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, struct module *owner) { int minor; @@ -231,61 +233,80 @@ int __must_check media_devnode_register(struct media_devnode *mdev, if (minor == MEDIA_NUM_DEVICES) { mutex_unlock(&media_devnode_lock); pr_err("could not get a free minor\n"); + kfree(devnode); return -ENFILE; } set_bit(minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); - mdev->minor = minor; + devnode->minor = minor; + devnode->media_dev = mdev; + + /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ + devnode->dev.bus = &media_bus_type; + devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); + devnode->dev.release = media_devnode_release; + if (devnode->parent) + devnode->dev.parent = devnode->parent; + dev_set_name(&devnode->dev, "media%d", devnode->minor); + device_initialize(&devnode->dev); /* Part 2: Initialize and register the character device */ - cdev_init(&mdev->cdev, &media_devnode_fops); - mdev->cdev.owner = owner; + cdev_init(&devnode->cdev, &media_devnode_fops); + devnode->cdev.owner = owner; + devnode->cdev.kobj.parent = &devnode->dev.kobj; - ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); + ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); if (ret < 0) { pr_err("%s: cdev_add failed\n", __func__); - goto error; + goto cdev_add_error; } - /* Part 3: Register the media device */ - mdev->dev.bus = &media_bus_type; - mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); - mdev->dev.release = media_devnode_release; - if (mdev->parent) - mdev->dev.parent = mdev->parent; - dev_set_name(&mdev->dev, "media%d", mdev->minor); - ret = device_register(&mdev->dev); + /* Part 3: Add the media device */ + ret = device_add(&devnode->dev); if (ret < 0) { - pr_err("%s: device_register failed\n", __func__); - goto error; + pr_err("%s: device_add failed\n", __func__); + goto device_add_error; } /* Part 4: Activate this minor. The char device can now be used. */ - set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); return 0; -error: +device_add_error: + cdev_del(&devnode->cdev); +cdev_add_error: mutex_lock(&media_devnode_lock); - cdev_del(&mdev->cdev); - clear_bit(mdev->minor, media_devnode_nums); + clear_bit(devnode->minor, media_devnode_nums); + devnode->media_dev = NULL; mutex_unlock(&media_devnode_lock); + put_device(&devnode->dev); return ret; } -void media_devnode_unregister(struct media_devnode *mdev) +void media_devnode_unregister_prepare(struct media_devnode *devnode) { - /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(mdev)) + /* Check if devnode was ever registered at all */ + if (!media_devnode_is_registered(devnode)) return; mutex_lock(&media_devnode_lock); - clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + mutex_unlock(&media_devnode_lock); +} + +void media_devnode_unregister(struct media_devnode *devnode) +{ + mutex_lock(&media_devnode_lock); + /* Delete the cdev on this minor as well */ + cdev_del(&devnode->cdev); mutex_unlock(&media_devnode_lock); - device_unregister(&mdev->dev); + device_del(&devnode->dev); + devnode->media_dev = NULL; + put_device(&devnode->dev); } /* diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index da8b414fd824..8681b9143a35 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -655,7 +655,6 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct static int dst_ca_open(struct inode *inode, struct file *file) { dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); - try_module_get(THIS_MODULE); return 0; } @@ -663,7 +662,6 @@ static int dst_ca_open(struct inode *inode, struct file *file) static int dst_ca_release(struct inode *inode, struct file *file) { dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); - module_put(THIS_MODULE); return 0; } diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 8d6f04fc8013..476f7f0dcf81 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -492,7 +492,6 @@ static int cobalt_subdevs_init(struct cobalt *cobalt) .ain_sel = ADV7604_AIN7_8_9_NC_SYNC_3_1, .bus_order = ADV7604_BUS_ORDER_BRG, .blank_data = 1, - .op_656_range = 1, .op_format_mode_sel = ADV7604_OP_FORMAT_MODE0, .int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH, .dr_str_data = ADV76XX_DR_STR_HIGH, @@ -571,7 +570,6 @@ static int cobalt_subdevs_hsma_init(struct cobalt *cobalt) .bus_order = ADV7842_BUS_ORDER_RBG, .op_format_mode_sel = ADV7842_OP_FORMAT_MODE0, .blank_data = 1, - .op_656_range = 1, .dr_str_data = 3, .dr_str_clk = 3, .dr_str_sync = 3, @@ -691,17 +689,10 @@ static int cobalt_probe(struct pci_dev *pci_dev, cobalt->pci_dev = pci_dev; cobalt->instance = i; - cobalt->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(cobalt->alloc_ctx)) { - kfree(cobalt); - return -ENOMEM; - } - retval = v4l2_device_register(&pci_dev->dev, &cobalt->v4l2_dev); if (retval) { pr_err("cobalt: v4l2_device_register of card %d failed\n", cobalt->instance); - vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); kfree(cobalt); return retval; } @@ -782,7 +773,6 @@ static int cobalt_probe(struct pci_dev *pci_dev, cobalt_err("error %d on initialization\n", retval); v4l2_device_unregister(&cobalt->v4l2_dev); - vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); kfree(cobalt); return retval; } @@ -818,7 +808,6 @@ static void cobalt_remove(struct pci_dev *pci_dev) cobalt_info("removed cobalt card\n"); v4l2_device_unregister(v4l2_dev); - vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); kfree(cobalt); } diff --git a/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h index b2f08e4a68bf..ed00dc9d9399 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.h +++ b/drivers/media/pci/cobalt/cobalt-driver.h @@ -262,7 +262,6 @@ struct cobalt { int instance; struct pci_dev *pci_dev; struct v4l2_device v4l2_dev; - void *alloc_ctx; void __iomem *bar0, *bar1; diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index c0ba458f6cf3..d05672fe9ff9 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -45,7 +45,7 @@ static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60; static int cobalt_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cobalt_stream *s = q->drv_priv; unsigned size = s->stride * s->height; @@ -54,7 +54,6 @@ static int cobalt_queue_setup(struct vb2_queue *q, *num_buffers = 3; if (*num_buffers > NR_BUFS) *num_buffers = NR_BUFS; - alloc_ctxs[0] = s->cobalt->alloc_ctx; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; *num_planes = 1; @@ -1224,6 +1223,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 2; q->lock = &s->lock; + q->dev = &cobalt->pci_dev->dev; vdev->queue = q; video_set_drvdata(vdev, s); diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c index 341bddc00b77..284275270f1b 100644 --- a/drivers/media/pci/cx18/cx18-alsa-mixer.c +++ b/drivers/media/pci/cx18/cx18-alsa-mixer.c @@ -93,7 +93,7 @@ static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); snd_cx18_lock(cxsc); - ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl); snd_cx18_unlock(cxsc); if (!ret) @@ -115,14 +115,14 @@ static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, snd_cx18_lock(cxsc); /* Fetch current state */ - ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl); if (ret || (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { /* Set, if needed */ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); - ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl); + ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl); if (!ret) ret = 1; /* Indicate control was changed w/o error */ } diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 260e462d91b4..2f23b26b16c0 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -560,7 +560,7 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = VBI_ACTIVE_SAMPLES * 36; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h index 47ce80fa73b9..ef308a10e870 100644 --- a/drivers/media/pci/cx18/cx18-driver.h +++ b/drivers/media/pci/cx18/cx18-driver.h @@ -492,9 +492,9 @@ struct cx18_card; * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples */ -static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */ -static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */ -static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */ +#define VBI_ACTIVE_SAMPLES 1444 /* 4 byte SAV + 720 Y + 720 U/V */ +#define VBI_HBLANK_SAMPLES_60HZ 272 /* 4 byte EAV + 268 anc/fill */ +#define VBI_HBLANK_SAMPLES_50HZ 284 /* 4 byte EAV + 280 anc/fill */ #define CX18_VBI_FRAMES 32 diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index eeb741c7db1b..fecca2a63891 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -177,7 +177,7 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, vbifmt->sampling_rate = 27000000; vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */ - vbifmt->samples_per_line = vbi_active_samples - 4; + vbifmt->samples_per_line = VBI_ACTIVE_SAMPLES - 4; vbifmt->sample_format = V4L2_PIX_FMT_GREY; vbifmt->start[0] = cx->vbi.start[0]; vbifmt->start[1] = cx->vbi.start[1]; diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index c9860845264f..f3802ec1b383 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -605,9 +605,9 @@ static void cx18_vbi_setup(struct cx18_stream *s) /* Lines per field */ data[1] = (lines / 2) | ((lines / 2) << 16); /* bytes per line */ - data[2] = (raw ? vbi_active_samples - : (cx->is_60hz ? vbi_hblank_samples_60Hz - : vbi_hblank_samples_50Hz)); + data[2] = (raw ? VBI_ACTIVE_SAMPLES + : (cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ + : VBI_HBLANK_SAMPLES_50HZ)); /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[3] = 1; @@ -761,7 +761,7 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s) s->bufs_per_mdl = 1; if (cx18_raw_vbi(s->cx)) { s->mdl_size = (s->cx->is_60hz ? 12 : 18) - * 2 * vbi_active_samples; + * 2 * VBI_ACTIVE_SAMPLES; } else { /* * See comment in cx18_vbi_setup() below about the @@ -769,8 +769,8 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s) * the lines on which EAV RP codes toggle. */ s->mdl_size = s->cx->is_60hz - ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz - : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; + ? (21 - 4 + 1) * 2 * VBI_HBLANK_SAMPLES_60HZ + : (23 - 2 + 1) * 2 * VBI_HBLANK_SAMPLES_50HZ; } break; default: diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c index add99642f1e2..43360cbcf24b 100644 --- a/drivers/media/pci/cx18/cx18-vbi.c +++ b/drivers/media/pci/cx18/cx18-vbi.c @@ -108,7 +108,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) /* FIXME - this function ignores the input size. */ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { - u32 line_size = vbi_active_samples; + u32 line_size = VBI_ACTIVE_SAMPLES; u32 lines = cx->vbi.count * 2; u8 *q = buf; u8 *p; @@ -145,8 +145,8 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, struct v4l2_decode_vbi_line vbi; int i; u32 line = 0; - u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz - : vbi_hblank_samples_50Hz; + u32 line_size = cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ + : VBI_HBLANK_SAMPLES_50HZ; /* find the first valid line */ for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index bd333875a1f7..efec2d1a7afd 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1140,7 +1140,7 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder) static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx23885_dev *dev = q->drv_priv; @@ -1148,7 +1148,6 @@ static int queue_setup(struct vb2_queue *q, dev->ts1.ts_packet_count = mpeglines; *num_planes = 1; sizes[0] = mpeglinesize * mpeglines; - alloc_ctxs[0] = dev->alloc_ctx; *num_buffers = mpegbufs; return 0; } diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 310ee769aed4..4abf50f2694f 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -765,6 +765,11 @@ struct cx23885_board cx23885_boards[] = { .amux = CX25840_AUDIO7, } }, }, + [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB] = { + .name = "Hauppauge WinTV-QuadHD-DVB", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -1060,6 +1065,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1576, .subdevice = 0x0460, .card = CX23885_BOARD_VIEWCAST_460E, + }, { + .subvendor = 0x0070, + .subdevice = 0x6a28, + .card = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB, /* Tuner Pair 1 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x6b28, + .card = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB, /* Tuner Pair 2 */ }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1257,6 +1270,14 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 150329: /* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */ break; + case 166100: + /* WinTV-QuadHD (DVB) Tuner Pair 1 (PCIe, IR, half height, + DVB-T/T2/C, DVB-T/T2/C */ + break; + case 166101: + /* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height, + DVB-T/T2/C, DVB-T/T2/C */ + break; default: printk(KERN_WARNING "%s: warning: " "unknown hauppauge model #%d\n", @@ -1729,20 +1750,22 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_2); break; case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: /* - * GPIO-00 IR_WIDE - * GPIO-02 wake# - * GPIO-03 VAUX Pres. - * GPIO-07 PROG# - * GPIO-08 SAT_RESN - * GPIO-09 TER_RESN - * GPIO-10 B2_SENSE - * GPIO-11 B1_SENSE - * GPIO-15 IR_LED_STATUS - * GPIO-19 IR_NARROW - * GPIO-20 Blauster1 - * ALTGPIO VAUX_SWITCH - * AUX_PLL_CLK : Blaster2 + * HVR5525 GPIO Details: + * GPIO-00 IR_WIDE + * GPIO-02 wake# + * GPIO-03 VAUX Pres. + * GPIO-07 PROG# + * GPIO-08 SAT_RESN + * GPIO-09 TER_RESN + * GPIO-10 B2_SENSE + * GPIO-11 B1_SENSE + * GPIO-15 IR_LED_STATUS + * GPIO-19 IR_NARROW + * GPIO-20 Blauster1 + * ALTGPIO VAUX_SWITCH + * AUX_PLL_CLK : Blaster2 */ /* Put the parts into reset and back */ cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); @@ -1802,6 +1825,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: /* FIXME: Implement me */ break; case CX23885_BOARD_HAUPPAUGE_HVR1270: @@ -2000,6 +2024,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_STARBURST: case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -2145,6 +2170,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 813c217b5e1a..c86b1093ab99 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2005,14 +2005,9 @@ static int cx23885_initdev(struct pci_dev *pci_dev, err = pci_set_dma_mask(pci_dev, 0xffffffff); if (err) { printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); - goto fail_context; + goto fail_ctrl; } - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail_context; - } err = request_irq(pci_dev->irq, cx23885_irq, IRQF_SHARED, dev->name, dev); if (err < 0) { @@ -2041,8 +2036,6 @@ static int cx23885_initdev(struct pci_dev *pci_dev, return 0; fail_irq: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); -fail_context: cx23885_dev_unregister(dev); fail_ctrl: v4l2_ctrl_handler_free(hdl); @@ -2068,7 +2061,6 @@ static void cx23885_finidev(struct pci_dev *pci_dev) pci_disable_device(pci_dev); cx23885_dev_unregister(dev); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(v4l2_dev); kfree(dev); diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index f041b6931ba8..e5748a93c479 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -94,7 +94,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx23885_tsport *port = q->drv_priv; @@ -102,7 +102,6 @@ static int queue_setup(struct vb2_queue *q, port->ts_packet_count = 32; *num_planes = 1; sizes[0] = port->ts_packet_size * port->ts_packet_count; - alloc_ctxs[0] = port->dev->alloc_ctx; *num_buffers = 32; return 0; } @@ -2269,9 +2268,107 @@ static int dvb_register(struct cx23885_tsport *port) } break; } + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + switch (port->nr) { + /* port b - Terrestrial/cable */ + case 1: + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module("%s", info.type); + client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x66; + info.platform_data = &si2168_config; + request_module("%s", info.type); + client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x62; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; + default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " - " isn't supported yet\n", + " isn't supported yet\n", dev->name); break; } @@ -2397,6 +2494,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c index 39750ebcc04c..75e7fa7b1121 100644 --- a/drivers/media/pci/cx23885/cx23885-vbi.c +++ b/drivers/media/pci/cx23885/cx23885-vbi.c @@ -122,7 +122,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx23885_dev *dev = q->drv_priv; unsigned lines = VBI_PAL_LINE_COUNT; @@ -131,7 +131,6 @@ static int queue_setup(struct vb2_queue *q, lines = VBI_NTSC_LINE_COUNT; *num_planes = 1; sizes[0] = lines * VBI_LINE_LENGTH * 2; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index e1d7d0847167..6d735222a958 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -335,13 +335,12 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx23885_dev *dev = q->drv_priv; *num_planes = 1; sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } @@ -1268,6 +1267,7 @@ int cx23885_video_register(struct cx23885_dev *dev) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) @@ -1284,6 +1284,7 @@ int cx23885_video_register(struct cx23885_dev *dev) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index b1a5409408c7..24a0a6c5b501 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -103,6 +103,7 @@ #define CX23885_BOARD_HAUPPAUGE_STARBURST 53 #define CX23885_BOARD_VIEWCAST_260E 54 #define CX23885_BOARD_VIEWCAST_460E 55 +#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -430,7 +431,6 @@ struct cx23885_dev { struct vb2_queue vb2_vidq; struct cx23885_dmaqueue vbiq; struct vb2_queue vb2_vbiq; - void *alloc_ctx; spinlock_t slock; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index b602eba2b601..df189b16af12 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -693,7 +693,7 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, * Only boards with eeprom and byte 1 at eeprom=1 have it */ -static const struct pci_device_id cx25821_audio_pci_tbl[] = { +static const struct pci_device_id __maybe_unused cx25821_audio_pci_tbl[] = { {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0,} }; diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 0042803a9de7..9a5f912ca859 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1301,15 +1301,10 @@ static int cx25821_initdev(struct pci_dev *pci_dev, goto fail_unregister_device; } - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail_unregister_pci; - } err = cx25821_dev_setup(dev); if (err) - goto fail_free_ctx; + goto fail_unregister_pci; /* print pci info */ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); @@ -1340,8 +1335,6 @@ static int cx25821_initdev(struct pci_dev *pci_dev, pr_info("cx25821_initdev() can't get IRQ !\n"); cx25821_dev_unregister(dev); -fail_free_ctx: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); fail_unregister_pci: pci_disable_device(pci_dev); fail_unregister_device: @@ -1365,7 +1358,6 @@ static void cx25821_finidev(struct pci_dev *pci_dev) free_irq(pci_dev->irq, dev); cx25821_dev_unregister(dev); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index c48bba9daf1f..adcd09be347d 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -143,13 +143,11 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) static int cx25821_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx25821_channel *chan = q->drv_priv; unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3; - alloc_ctxs[0] = chan->dev->alloc_ctx; - if (*num_planes) return sizes[0] < size ? -EINVAL : 0; @@ -759,6 +757,7 @@ int cx25821_video_register(struct cx25821_dev *dev) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; if (!is_output) { err = vb2_queue_init(q); diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index a513b68be0fa..35c7375e4617 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -249,7 +249,6 @@ struct cx25821_dev { int hwrevision; /* used by cx25821-alsa */ struct snd_card *card; - void *alloc_ctx; u32 clk_freq; diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index e158a1da1d41..f3f13eb0c16e 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -799,13 +799,9 @@ static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; - struct v4l2_control client_ctl; - - memset(&client_ctl, 0, sizeof(client_ctl)); - client_ctl.value = 0 != value->value.integer.value[0]; - client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; - call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + wm8775_s_ctrl(core, V4L2_CID_AUDIO_LOUDNESS, + value->value.integer.value[0] != 0); return 0; } diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index 3233d45d1e5b..04fe9af2a802 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -639,7 +639,7 @@ static int blackbird_stop_codec(struct cx8802_dev *dev) static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx8802_dev *dev = q->drv_priv; @@ -647,7 +647,6 @@ static int queue_setup(struct vb2_queue *q, dev->ts_packet_size = 188 * 4; dev->ts_packet_count = 32; sizes[0] = dev->ts_packet_size * dev->ts_packet_count; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } @@ -1183,6 +1182,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &core->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 851d2a9caed3..5bb63e7a5691 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -84,7 +84,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx8802_dev *dev = q->drv_priv; @@ -92,7 +92,6 @@ static int queue_setup(struct vb2_queue *q, dev->ts_packet_size = 188 * 4; dev->ts_packet_count = dvb_buf_tscnt; sizes[0] = dev->ts_packet_size * dev->ts_packet_count; - alloc_ctxs[0] = dev->alloc_ctx; *num_buffers = dvb_buf_tscnt; return 0; } @@ -1793,6 +1792,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &core->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index f34c229f9b37..245357adbc25 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -726,11 +726,6 @@ static int cx8802_probe(struct pci_dev *pci_dev, if (NULL == dev) goto fail_core; dev->pci = pci_dev; - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail_dev; - } dev->core = core; /* Maintain a reference so cx88-video can query the 8802 device. */ @@ -738,7 +733,7 @@ static int cx8802_probe(struct pci_dev *pci_dev, err = cx8802_init_common(dev); if (err != 0) - goto fail_free; + goto fail_dev; INIT_LIST_HEAD(&dev->drvlist); mutex_lock(&cx8802_mutex); @@ -749,8 +744,6 @@ static int cx8802_probe(struct pci_dev *pci_dev, request_modules(dev); return 0; - fail_free: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); fail_dev: kfree(dev); fail_core: @@ -798,7 +791,6 @@ static void cx8802_remove(struct pci_dev *pci_dev) /* common */ cx8802_fini_common(dev); cx88_core_put(dev->core,dev->pci); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); kfree(dev); } diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c index ccc646d819f2..d3237cf8ffa3 100644 --- a/drivers/media/pci/cx88/cx88-vbi.c +++ b/drivers/media/pci/cx88/cx88-vbi.c @@ -109,7 +109,7 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev, static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx8800_dev *dev = q->drv_priv; @@ -118,7 +118,6 @@ static int queue_setup(struct vb2_queue *q, sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2; else sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index 5f331df65fb9..5dc1e3f08d50 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -431,14 +431,13 @@ static int restart_video_queue(struct cx8800_dev *dev, static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cx8800_dev *dev = q->drv_priv; struct cx88_core *core = dev->core; *num_planes = 1; sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } @@ -1319,12 +1318,6 @@ static int cx8800_initdev(struct pci_dev *pci_dev, printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); goto fail_core; } - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail_core; - } - /* initialize driver struct */ spin_lock_init(&dev->slock); @@ -1445,6 +1438,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &core->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) @@ -1461,6 +1455,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, q->mem_ops = &vb2_dma_sg_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &core->lock; + q->dev = &dev->pci->dev; err = vb2_queue_init(q); if (err < 0) @@ -1530,7 +1525,6 @@ static int cx8800_initdev(struct pci_dev *pci_dev, free_irq(pci_dev->irq, dev); mutex_unlock(&core->lock); fail_core: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); core->v4ldev = NULL; cx88_core_put(core,dev->pci); fail_free: @@ -1564,7 +1558,6 @@ static void cx8800_finidev(struct pci_dev *pci_dev) /* free memory */ cx88_core_put(core,dev->pci); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); kfree(dev); } diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index 78f817ee7e41..ecd4b7bece99 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -485,7 +485,6 @@ struct cx8800_dev { /* pci i/o */ struct pci_dev *pci; unsigned char pci_rev,pci_lat; - void *alloc_ctx; const struct cx8800_fmt *fmt; @@ -549,7 +548,6 @@ struct cx8802_dev { /* pci i/o */ struct pci_dev *pci; unsigned char pci_rev,pci_lat; - void *alloc_ctx; /* dma queues */ struct cx88_dmaqueue mpegq; diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 6e995ef8c37e..47def73b3502 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1569,10 +1569,9 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (pci_enable_device(pdev) < 0) return -ENODEV; - dev = vmalloc(sizeof(struct ddb)); + dev = vzalloc(sizeof(struct ddb)); if (dev == NULL) return -ENOMEM; - memset(dev, 0, sizeof(struct ddb)); dev->pdev = pdev; pci_set_drvdata(pdev, dev); diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 568c0c8fb2dc..6a219694b225 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -133,7 +133,7 @@ static int wait_i2c_reg(void __iomem *addr) static int dt3155_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct dt3155_priv *pd = vb2_get_drv_priv(vq); @@ -141,7 +141,6 @@ dt3155_queue_setup(struct vb2_queue *vq, if (vq->num_buffers + *nbuffers < 2) *nbuffers = 2 - vq->num_buffers; - alloc_ctxs[0] = pd->alloc_ctx; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; *num_planes = 1; @@ -544,21 +543,16 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) pd->vidq.min_buffers_needed = 2; pd->vidq.gfp_flags = GFP_DMA32; pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */ + pd->vidq.dev = &pdev->dev; pd->vdev.queue = &pd->vidq; err = vb2_queue_init(&pd->vidq); if (err < 0) goto err_v4l2_dev_unreg; - pd->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pd->alloc_ctx)) { - dev_err(&pdev->dev, "Can't allocate buffer context"); - err = PTR_ERR(pd->alloc_ctx); - goto err_v4l2_dev_unreg; - } spin_lock_init(&pd->lock); pd->config = ACQ_MODE_EVEN; err = pci_enable_device(pdev); if (err) - goto err_free_ctx; + goto err_v4l2_dev_unreg; err = pci_request_region(pdev, 0, pci_name(pdev)); if (err) goto err_pci_disable; @@ -588,8 +582,6 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_release_region(pdev, 0); err_pci_disable: pci_disable_device(pdev); -err_free_ctx: - vb2_dma_contig_cleanup_ctx(pd->alloc_ctx); err_v4l2_dev_unreg: v4l2_device_unregister(&pd->v4l2_dev); return err; @@ -608,7 +600,6 @@ static void dt3155_remove(struct pci_dev *pdev) pci_iounmap(pdev, pd->regs); pci_release_region(pdev, 0); pci_disable_device(pdev); - vb2_dma_contig_cleanup_ctx(pd->alloc_ctx); } static const struct pci_device_id pci_ids[] = { diff --git a/drivers/media/pci/dt3155/dt3155.h b/drivers/media/pci/dt3155/dt3155.h index b3531e0bc733..39442e58919d 100644 --- a/drivers/media/pci/dt3155/dt3155.h +++ b/drivers/media/pci/dt3155/dt3155.h @@ -161,7 +161,6 @@ * @vdev: video_device structure * @pdev: pointer to pci_dev structure * @vidq: vb2_queue structure - * @alloc_ctx: dma_contig allocation context * @curr_buf: pointer to curren buffer * @mux: mutex to protect the instance * @dmaq: queue for dma buffers @@ -181,7 +180,6 @@ struct dt3155_priv { struct video_device vdev; struct pci_dev *pdev; struct vb2_queue vidq; - struct vb2_alloc_ctx *alloc_ctx; struct vb2_v4l2_buffer *curr_buf; struct mutex mux; struct list_head dmaq; diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c index 33ec05b09af3..79b24bde4a39 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c @@ -93,7 +93,7 @@ static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl, vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); snd_ivtv_lock(itvsc); - ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl); + ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl); snd_ivtv_unlock(itvsc); if (!ret) @@ -115,14 +115,14 @@ static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl, snd_ivtv_lock(itvsc); /* Fetch current state */ - ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl); + ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl); if (ret || (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { /* Set, if needed */ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); - ret = v4l2_subdev_call(itv->sd_audio, core, s_ctrl, &vctrl); + ret = v4l2_s_ctrl(itv->sd_audio->ctrl_handler, &vctrl); if (!ret) ret = 1; /* Indicate control was changed w/o error */ } diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig index f277b0b10c2d..0ad37714c7fd 100644 --- a/drivers/media/pci/netup_unidvb/Kconfig +++ b/drivers/media/pci/netup_unidvb/Kconfig @@ -5,8 +5,13 @@ config DVB_NETUP_UNIDVB select VIDEOBUF2_VMALLOC select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT + select DVB_HELENE if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for NetUP PCI express Universal DVB card. - + help + Say Y when you want to support NetUP Dual Universal DVB card + Card can receive two independent streams in following standards: + DVB-S/S2, T/T2, C/C2 + Two CI slots available for CAM modules. diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h index a67b28111905..39b08ecda1fc 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb.h +++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h @@ -50,6 +50,15 @@ #define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) #define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) +/* NetUP Universal DVB card hardware revisions and it's PCI device id's: + * 1.3 - CXD2841ER demod, ASCOT2E and HORUS3A tuners + * 1.4 - CXD2854ER demod, HELENE tuner +*/ +enum netup_hw_rev { + NETUP_HW_REV_1_3 = 0x18F6, + NETUP_HW_REV_1_4 = 0x18F7 +}; + struct netup_dma { u8 num; spinlock_t lock; @@ -119,6 +128,7 @@ struct netup_unidvb_dev { struct netup_dma dma[2]; struct netup_ci_state ci[2]; struct netup_spi *spi; + enum netup_hw_rev rev; }; int netup_i2c_register(struct netup_unidvb_dev *ndev); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c index f46ffac66ee9..f535270c2116 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c @@ -147,7 +147,7 @@ static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, { struct netup_ci_state *state = en50221->data; struct netup_unidvb_dev *dev = state->dev; - u8 val = *((u8 __force *)state->membase8_io + addr); + u8 val = *((u8 __force *)state->membase8_config + addr); dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); @@ -162,7 +162,7 @@ static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); - *((u8 __force *)state->membase8_io + addr) = data; + *((u8 __force *)state->membase8_config + addr) = data; return 0; } diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 2b667b315913..ac547cb84de8 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -34,6 +34,7 @@ #include "cxd2841er.h" #include "horus3a.h" #include "ascot2e.h" +#include "helene.h" #include "lnbh25.h" static int spi_enable; @@ -120,7 +121,8 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); static void netup_unidvb_queue_cleanup(struct netup_dma *dma); static struct cxd2841er_config demod_config = { - .i2c_addr = 0xc8 + .i2c_addr = 0xc8, + .xtal = SONY_XTAL_24000 }; static struct horus3a_config horus3a_conf = { @@ -134,6 +136,12 @@ static struct ascot2e_config ascot2e_conf = { .set_tuner_callback = netup_unidvb_tuner_ctrl }; +static struct helene_config helene_conf = { + .i2c_address = 0xc0, + .xtal = SONY_HELENE_XTAL_24000, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + static struct lnbh25_config lnbh25_conf = { .i2c_address = 0x10, .data2_config = LNBH25_TEN | LNBH25_EXTM @@ -152,6 +160,11 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) __func__, dma->num, is_dvb_tc); reg = readb(ndev->bmmio0 + GPIO_REG_IO); mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; + + /* inverted tuner control in hw rev. 1.4 */ + if (ndev->rev == NETUP_HW_REV_1_4) + is_dvb_tc = !is_dvb_tc; + if (!is_dvb_tc) reg |= mask; else @@ -280,7 +293,7 @@ static int netup_unidvb_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) + struct device *alloc_devs[]) { struct netup_dma *dma = vb2_get_drv_priv(vq); @@ -372,7 +385,15 @@ static int netup_unidvb_queue_init(struct netup_dma *dma, static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, int num) { - struct vb2_dvb_frontend *fe0, *fe1, *fe2; + int fe_count = 2; + int i = 0; + struct vb2_dvb_frontend *fes[2]; + u8 fe_name[32]; + + if (ndev->rev == NETUP_HW_REV_1_3) + demod_config.xtal = SONY_XTAL_20500; + else + demod_config.xtal = SONY_XTAL_24000; if (num < 0 || num > 1) { dev_dbg(&ndev->pci_dev->dev, @@ -381,84 +402,96 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, } mutex_init(&ndev->frontends[num].lock); INIT_LIST_HEAD(&ndev->frontends[num].felist); - if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 2) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 3) == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to allocate vb2_dvb_frontend\n", - __func__); - return -ENOMEM; + + for (i = 0; i < fe_count; i++) { + if (vb2_dvb_alloc_frontend(&ndev->frontends[num], i+1) + == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to allocate vb2_dvb_frontend\n", + __func__); + return -ENOMEM; + } } - fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1); - fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2); - fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3); - if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): frontends has not been allocated\n", __func__); - return -EINVAL; + + for (i = 0; i < fe_count; i++) { + fes[i] = vb2_dvb_get_frontend(&ndev->frontends[num], i+1); + if (fes[i] == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): frontends has not been allocated\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < fe_count; i++) { + netup_unidvb_queue_init(&ndev->dma[num], &fes[i]->dvb.dvbq); + snprintf(fe_name, sizeof(fe_name), "netup_fe%d", i); + fes[i]->dvb.name = fe_name; } - netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq); - fe0->dvb.name = "netup_fe0"; - fe1->dvb.name = "netup_fe1"; - fe2->dvb.name = "netup_fe2"; - fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s, + + fes[0]->dvb.frontend = dvb_attach(cxd2841er_attach_s, &demod_config, &ndev->i2c[num].adap); - if (fe0->dvb.frontend == NULL) { + if (fes[0]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach DVB-S/S2 frontend\n", __func__); goto frontend_detach; } - horus3a_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, - &horus3a_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-S/S2 tuner frontend\n", - __func__); - goto frontend_detach; + + if (ndev->rev == NETUP_HW_REV_1_3) { + horus3a_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(horus3a_attach, fes[0]->dvb.frontend, + &horus3a_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach HORUS3A DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach_s, fes[0]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } } - if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, + + if (!dvb_attach(lnbh25_attach, fes[0]->dvb.frontend, &lnbh25_conf, &ndev->i2c[num].adap)) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach SEC frontend\n", __func__); goto frontend_detach; } + /* DVB-T/T2 frontend */ - fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t, + fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t_c, &demod_config, &ndev->i2c[num].adap); - if (fe1->dvb.frontend == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T frontend\n", __func__); - goto frontend_detach; - } - fe1->dvb.frontend->id = 1; - ascot2e_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T tuner frontend\n", - __func__); - goto frontend_detach; - } - /* DVB-C/C2 frontend */ - fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c, - &demod_config, &ndev->i2c[num].adap); - if (fe2->dvb.frontend == NULL) { + if (fes[1]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-C frontend\n", __func__); + "%s(): unable to attach Ter frontend\n", __func__); goto frontend_detach; } - fe2->dvb.frontend->id = 2; - if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T/C tuner frontend\n", - __func__); - goto frontend_detach; + fes[1]->dvb.frontend->id = 1; + if (ndev->rev == NETUP_HW_REV_1_3) { + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(ascot2e_attach, fes[1]->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach Ter tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach, fes[1]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE Ter tuner frontend\n", + __func__); + goto frontend_detach; + } } if (vb2_dvb_register_bus(&ndev->frontends[num], @@ -730,7 +763,7 @@ static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) static int netup_unidvb_request_modules(struct device *dev) { static const char * const modules[] = { - "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL + "lnbh25", "ascot2e", "horus3a", "cxd2841er", "helene", NULL }; const char * const *curr_mod = modules; int err; @@ -774,6 +807,16 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, if (!ndev) goto dev_alloc_err; + /* detect hardware revision */ + if (pci_dev->device == NETUP_HW_REV_1_3) + ndev->rev = NETUP_HW_REV_1_3; + else + ndev->rev = NETUP_HW_REV_1_4; + + dev_info(&pci_dev->dev, + "%s(): board (0x%x) hardware revision 0x%x\n", + __func__, pci_dev->device, ndev->rev); + ndev->old_fw = old_firmware; ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME); if (!ndev->wq) { @@ -932,7 +975,7 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, kfree(ndev); dev_alloc_err: dev_err(&pci_dev->dev, - "%s(): failed to initizalize device\n", __func__); + "%s(): failed to initialize device\n", __func__); return -EIO; } @@ -972,7 +1015,8 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev) static struct pci_device_id netup_unidvb_pci_tbl[] = { - { PCI_DEVICE(0x1b55, 0x18f6) }, + { PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */ + { PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */ { 0, } }; MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index c0e1780ec831..ffb66a9ae23e 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -1164,18 +1164,13 @@ static int saa7134_initdev(struct pci_dev *pci_dev, saa7134_board_init1(dev); saa7134_hwinit1(dev); - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail3; - } /* get irq */ err = request_irq(pci_dev->irq, saa7134_irq, IRQF_SHARED, dev->name, dev); if (err < 0) { pr_err("%s: can't get IRQ %d\n", dev->name,pci_dev->irq); - goto fail4; + goto fail3; } /* wait a bit, register i2c bus */ @@ -1233,7 +1228,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, if (err < 0) { pr_info("%s: can't register video device\n", dev->name); - goto fail5; + goto fail4; } pr_info("%s: registered device %s [v4l2]\n", dev->name, video_device_node_name(dev->video_dev)); @@ -1246,7 +1241,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, vbi_nr[dev->nr]); if (err < 0) - goto fail5; + goto fail4; pr_info("%s: registered device %s\n", dev->name, video_device_node_name(dev->vbi_dev)); @@ -1257,7 +1252,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, radio_nr[dev->nr]); if (err < 0) - goto fail5; + goto fail4; pr_info("%s: registered device %s\n", dev->name, video_device_node_name(dev->radio_dev)); } @@ -1268,7 +1263,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, err = v4l2_mc_create_media_graph(dev->media_dev); if (err) { pr_err("failed to create media graph\n"); - goto fail5; + goto fail4; } #endif /* everything worked */ @@ -1287,17 +1282,15 @@ static int saa7134_initdev(struct pci_dev *pci_dev, #ifdef CONFIG_MEDIA_CONTROLLER err = media_device_register(dev->media_dev); if (err) - goto fail5; + goto fail4; #endif return 0; - fail5: + fail4: saa7134_unregister_video(dev); saa7134_i2c_unregister(dev); free_irq(pci_dev->irq, dev); - fail4: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); fail3: saa7134_hwfini(dev); iounmap(dev->lmmio); @@ -1367,7 +1360,6 @@ static void saa7134_finidev(struct pci_dev *pci_dev) /* release resources */ free_irq(pci_dev->irq, dev); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); iounmap(dev->lmmio); release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c index 0584a2adbe99..7eaf36a41db9 100644 --- a/drivers/media/pci/saa7134/saa7134-ts.c +++ b/drivers/media/pci/saa7134/saa7134-ts.c @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare); int saa7134_ts_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct saa7134_dmaqueue *dmaq = q->drv_priv; struct saa7134_dev *dev = dmaq->dev; @@ -131,7 +131,6 @@ int saa7134_ts_queue_setup(struct vb2_queue *q, *nbuffers = 3; *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup); diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index e76da37c4a8a..cf9a31e0a390 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -140,7 +140,7 @@ static int buffer_prepare(struct vb2_buffer *vb2) static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct saa7134_dmaqueue *dmaq = q->drv_priv; struct saa7134_dev *dev = dmaq->dev; @@ -155,7 +155,6 @@ static int queue_setup(struct vb2_queue *q, *nbuffers = saa7134_buffer_count(size, *nbuffers); *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = dev->alloc_ctx; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index ffa39543eb65..8a6ebd087889 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -963,7 +963,7 @@ static int buffer_prepare(struct vb2_buffer *vb2) static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct saa7134_dmaqueue *dmaq = q->drv_priv; struct saa7134_dev *dev = dmaq->dev; @@ -980,7 +980,6 @@ static int queue_setup(struct vb2_queue *q, *nbuffers = saa7134_buffer_count(size, *nbuffers); *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = dev->alloc_ctx; saa7134_enable_analog_tuner(dev); @@ -2173,6 +2172,7 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->buf_struct_size = sizeof(struct saa7134_buf); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) return ret; @@ -2191,6 +2191,7 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->buf_struct_size = sizeof(struct saa7134_buf); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->lock; + q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) return ret; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 69a9bbf22d4d..3849083526a7 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -610,7 +610,6 @@ struct saa7134_dev { /* video+ts+vbi capture */ - void *alloc_ctx; struct saa7134_dmaqueue video_q; struct vb2_queue video_vbq; struct saa7134_dmaqueue vbi_q; @@ -854,7 +853,7 @@ int saa7134_ts_buffer_init(struct vb2_buffer *vb2); int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2); int saa7134_ts_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]); + unsigned int sizes[], struct device *alloc_devs[]); int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count); void saa7134_ts_stop_streaming(struct vb2_queue *vq); diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c index 1b184c39ba97..32a353d162e7 100644 --- a/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/drivers/media/pci/saa7164/saa7164-encoder.c @@ -1022,8 +1022,7 @@ int saa7164_encoder_register(struct saa7164_port *port) dprintk(DBGLVL_ENC, "%s()\n", __func__); - if (port->type != SAA7164_MPEG_ENCODER) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_ENCODER); /* Sanity check that the PCI configuration space is active */ if (port->hwcfg.BARLocation == 0) { @@ -1151,8 +1150,7 @@ void saa7164_encoder_unregister(struct saa7164_port *port) dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); - if (port->type != SAA7164_MPEG_ENCODER) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_ENCODER); if (port->v4l_device) { if (port->v4l_device->minor != -1) diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h index 8337524bfb8c..97411b0384c1 100644 --- a/drivers/media/pci/saa7164/saa7164.h +++ b/drivers/media/pci/saa7164/saa7164.h @@ -263,10 +263,6 @@ struct saa7164_i2c { u32 i2c_rc; }; -struct saa7164_ctrl { - struct v4l2_queryctrl v; -}; - struct saa7164_tvnorm { char *name; v4l2_std_id id; diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 67a14c41c227..399164314c28 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -33,7 +33,7 @@ #include "solo6x10-jpeg.h" #define MIN_VID_BUFFERS 2 -#define FRAME_BUF_SIZE (196 * 1024) +#define FRAME_BUF_SIZE (400 * 1024) #define MP4_QS 16 #define DMA_ALIGN 4096 @@ -664,12 +664,9 @@ static int solo_ring_thread(void *data) static int solo_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], - void *alloc_ctxs[]) + struct device *alloc_devs[]) { - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); - sizes[0] = FRAME_BUF_SIZE; - alloc_ctxs[0] = solo_enc->alloc_ctx; *num_planes = 1; if (*num_buffers < MIN_VID_BUFFERS) @@ -1239,11 +1236,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, return ERR_PTR(-ENOMEM); hdl = &solo_enc->hdl; - solo_enc->alloc_ctx = vb2_dma_sg_init_ctx(&solo_dev->pdev->dev); - if (IS_ERR(solo_enc->alloc_ctx)) { - ret = PTR_ERR(solo_enc->alloc_ctx); - goto hdl_free; - } v4l2_ctrl_handler_init(hdl, 10); v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); @@ -1299,6 +1291,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); solo_enc->vidq.lock = &solo_enc->lock; + solo_enc->vidq.dev = &solo_dev->pdev->dev; ret = vb2_queue_init(&solo_enc->vidq); if (ret) goto hdl_free; @@ -1347,7 +1340,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->desc_items, solo_enc->desc_dma); hdl_free: v4l2_ctrl_handler_free(hdl); - vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx); kfree(solo_enc); return ERR_PTR(ret); } @@ -1362,7 +1354,6 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc) solo_enc->desc_items, solo_enc->desc_dma); video_unregister_device(solo_enc->vfd); v4l2_ctrl_handler_free(&solo_enc->hdl); - vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx); kfree(solo_enc); } diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 721ff5320de7..b4be47969b6b 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -315,12 +315,11 @@ static void solo_stop_thread(struct solo_dev *solo_dev) static int solo_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct solo_dev *solo_dev = vb2_get_drv_priv(q); sizes[0] = solo_image_size(solo_dev); - alloc_ctxs[0] = solo_dev->alloc_ctx; *num_planes = 1; if (*num_buffers < MIN_VID_BUFFERS) @@ -386,26 +385,24 @@ static int solo_querycap(struct file *file, void *priv, static int solo_enum_ext_input(struct solo_dev *solo_dev, struct v4l2_input *input) { - static const char * const dispnames_1[] = { "4UP" }; - static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char * const dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char * const *dispnames; + int ext = input->index - solo_dev->nr_chans; + unsigned int nup, first; - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + if (ext >= solo_dev->nr_ext) return -EINVAL; - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - + nup = (ext == 4) ? 16 : 4; + first = (ext & 3) << 2; /* first channel in the n-up */ + snprintf(input->name, sizeof(input->name), + "Multi %d-up (cameras %d-%d)", + nup, first + 1, first + nup); + /* Possible outputs: + * Multi 4-up (cameras 1-4) + * Multi 4-up (cameras 5-8) + * Multi 4-up (cameras 9-12) + * Multi 4-up (cameras 13-16) + * Multi 16-up (cameras 1-16) + */ return 0; } @@ -681,16 +678,11 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) solo_dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM; solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); solo_dev->vidq.lock = &solo_dev->lock; + solo_dev->vidq.dev = &solo_dev->pdev->dev; ret = vb2_queue_init(&solo_dev->vidq); if (ret < 0) goto fail; - solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); - if (IS_ERR(solo_dev->alloc_ctx)) { - dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); - return PTR_ERR(solo_dev->alloc_ctx); - } - /* Cycle all the channels and clear */ for (i = 0; i < solo_dev->nr_chans; i++) { solo_v4l2_set_ch(solo_dev, i); @@ -718,7 +710,6 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) fail: video_device_release(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); v4l2_ctrl_handler_free(&solo_dev->disp_hdl); solo_dev->vfd = NULL; return ret; @@ -730,7 +721,6 @@ void solo_v4l2_exit(struct solo_dev *solo_dev) return; video_unregister_device(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); v4l2_ctrl_handler_free(&solo_dev->disp_hdl); solo_dev->vfd = NULL; } diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 4ab6586c0467..5bd498735a66 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -178,7 +178,6 @@ struct solo_enc_dev { u32 sequence; struct vb2_queue vidq; struct list_head vidq_active; - void *alloc_ctx; int desc_count; int desc_nelts; struct solo_p2m_desc *desc_items; @@ -269,7 +268,6 @@ struct solo_dev { /* Buffer handling */ struct vb2_queue vidq; - struct vb2_alloc_ctx *alloc_ctx; u32 sequence; struct task_struct *kthread; struct mutex lock; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 1fc195f89686..aeb2b4e2db35 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -111,7 +111,6 @@ static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) * @input: input line for video signal ( 0 or 1 ) * @disabled: Device is in power down state * @slock: for excluse acces of registers - * @alloc_ctx: context for videobuf2 * @vb_vidq: queue maintained by videobuf2 layer * @buffer_list: list of buffer in use * @sequence: sequence number of acquired buffer @@ -141,7 +140,6 @@ struct sta2x11_vip { int disabled; spinlock_t slock; - struct vb2_alloc_ctx *alloc_ctx; struct vb2_queue vb_vidq; struct list_head buffer_list; unsigned int sequence; @@ -267,7 +265,7 @@ static void vip_active_buf_next(struct sta2x11_vip *vip) /* Videobuf2 Operations */ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct sta2x11_vip *vip = vb2_get_drv_priv(vq); @@ -276,7 +274,6 @@ static int queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = vip->format.sizeimage; - alloc_ctxs[0] = vip->alloc_ctx; vip->sequence = 0; vip->active = NULL; @@ -861,25 +858,15 @@ static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) vip->vb_vidq.ops = &vip_video_qops; vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vip->vb_vidq.dev = &vip->pdev->dev; err = vb2_queue_init(&vip->vb_vidq); if (err) return err; INIT_LIST_HEAD(&vip->buffer_list); spin_lock_init(&vip->lock); - - - vip->alloc_ctx = vb2_dma_contig_init_ctx(&vip->pdev->dev); - if (IS_ERR(vip->alloc_ctx)) { - v4l2_err(&vip->v4l2_dev, "Can't allocate buffer context"); - return PTR_ERR(vip->alloc_ctx); - } - return 0; } -static void sta2x11_vip_release_buffer(struct sta2x11_vip *vip) -{ - vb2_dma_contig_cleanup_ctx(vip->alloc_ctx); -} + static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) { /* @@ -1120,7 +1107,6 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, video_unregister_device(&vip->video_dev); free_irq(pdev->irq, vip); release_buf: - sta2x11_vip_release_buffer(vip); pci_disable_msi(pdev); unmap: vb2_queue_release(&vip->vb_vidq); diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index 4e77618fbb2b..8474528be91e 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -305,19 +305,13 @@ static int tw68_initdev(struct pci_dev *pci_dev, /* Then do any initialisation wanted before interrupts are on */ tw68_hw_init1(dev); - dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); - if (IS_ERR(dev->alloc_ctx)) { - err = PTR_ERR(dev->alloc_ctx); - goto fail3; - } - /* get irq */ err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq, IRQF_SHARED, dev->name, dev); if (err < 0) { pr_err("%s: can't get IRQ %d\n", dev->name, pci_dev->irq); - goto fail4; + goto fail3; } /* @@ -331,7 +325,7 @@ static int tw68_initdev(struct pci_dev *pci_dev, if (err < 0) { pr_err("%s: can't register video device\n", dev->name); - goto fail5; + goto fail4; } tw_setl(TW68_INTMASK, dev->pci_irqmask); @@ -340,10 +334,8 @@ static int tw68_initdev(struct pci_dev *pci_dev, return 0; -fail5: - video_unregister_device(&dev->vdev); fail4: - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); + video_unregister_device(&dev->vdev); fail3: iounmap(dev->lmmio); fail2: @@ -367,7 +359,6 @@ static void tw68_finidev(struct pci_dev *pci_dev) /* unregister */ video_unregister_device(&dev->vdev); v4l2_ctrl_handler_free(&dev->hdl); - vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); /* release resources */ iounmap(dev->lmmio); diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 07116a87a57b..5e8212845c87 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -378,7 +378,7 @@ static int tw68_buffer_count(unsigned int size, unsigned int count) static int tw68_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct tw68_dev *dev = vb2_get_drv_priv(q); unsigned tot_bufs = q->num_buffers + *num_buffers; @@ -388,7 +388,6 @@ static int tw68_queue_setup(struct vb2_queue *q, tot_bufs = 2; tot_bufs = tw68_buffer_count(size, tot_bufs); *num_buffers = tot_bufs - q->num_buffers; - alloc_ctxs[0] = dev->alloc_ctx; /* * We allow create_bufs, but only if the sizeimage is >= as the * current sizeimage. The tw68_buffer_count calculation becomes quite @@ -983,6 +982,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr) dev->vidq.buf_struct_size = sizeof(struct tw68_buf); dev->vidq.lock = &dev->lock; dev->vidq.min_buffers_needed = 2; + dev->vidq.dev = &dev->pci->dev; ret = vb2_queue_init(&dev->vidq); if (ret) return ret; diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h index 6c7dcb300f34..5585c7ee23f2 100644 --- a/drivers/media/pci/tw68/tw68.h +++ b/drivers/media/pci/tw68/tw68.h @@ -165,7 +165,6 @@ struct tw68_dev { unsigned field; struct vb2_queue vidq; struct list_head active; - void *alloc_ctx; /* various v4l controls */ const struct tw68_tvnorm *tvnorm; /* video */ diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig index fb8536974052..34ff37712313 100644 --- a/drivers/media/pci/tw686x/Kconfig +++ b/drivers/media/pci/tw686x/Kconfig @@ -3,6 +3,8 @@ config VIDEO_TW686X depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND depends on HAS_DMA select VIDEOBUF2_VMALLOC + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG select SND_PCM help Support for Intersil/Techwell TW686x-based frame grabber cards. diff --git a/drivers/media/pci/tw686x/tw686x-audio.c b/drivers/media/pci/tw686x/tw686x-audio.c index 91459ab715b2..96e444c49173 100644 --- a/drivers/media/pci/tw686x/tw686x-audio.c +++ b/drivers/media/pci/tw686x/tw686x-audio.c @@ -62,12 +62,22 @@ void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, } spin_unlock_irqrestore(&ac->lock, flags); + if (!done || !next) + continue; + /* + * Checking for a non-nil dma_desc[pb]->virt buffer is + * the same as checking for memcpy DMA mode. + */ desc = &ac->dma_descs[pb]; - if (done && next && desc->virt) { - memcpy(done->virt, desc->virt, desc->size); - ac->ptr = done->dma - ac->buf[0].dma; - snd_pcm_period_elapsed(ac->ss); + if (desc->virt) { + memcpy(done->virt, desc->virt, + dev->period_size); + } else { + u32 reg = pb ? ADMA_B_ADDR[ch] : ADMA_P_ADDR[ch]; + reg_write(dev, reg, next->dma); } + ac->ptr = done->dma - ac->buf[0].dma; + snd_pcm_period_elapsed(ac->ss); } } @@ -83,10 +93,9 @@ static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss) } /* - * The audio device rate is global and shared among all - * capture channels. The driver makes no effort to prevent - * rate modifications. User is free change the rate, but it - * means changing the rate for all capture sub-devices. + * Audio parameters are global and shared among all + * capture channels. The driver prevents changes to + * the parameters if any audio channel is capturing. */ static const struct snd_pcm_hardware tw686x_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -99,9 +108,9 @@ static const struct snd_pcm_hardware tw686x_capture_hw = { .rate_max = 48000, .channels_min = 1, .channels_max = 1, - .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, - .period_bytes_min = TW686X_AUDIO_PAGE_SZ, - .period_bytes_max = TW686X_AUDIO_PAGE_SZ, + .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, + .period_bytes_min = AUDIO_DMA_SIZE_MIN, + .period_bytes_max = AUDIO_DMA_SIZE_MAX, .periods_min = TW686X_AUDIO_PERIODS_MIN, .periods_max = TW686X_AUDIO_PERIODS_MAX, }; @@ -143,6 +152,14 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) int i; spin_lock_irqsave(&dev->lock, flags); + /* + * Given the audio parameters are global (i.e. shared across + * DMA channels), we need to check new params are allowed. + */ + if (((dev->audio_rate != rt->rate) || + (dev->period_size != period_size)) && dev->audio_enabled) + goto err_audio_busy; + tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); spin_unlock_irqrestore(&dev->lock, flags); @@ -156,12 +173,21 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) reg_write(dev, AUDIO_CONTROL2, reg); } - if (period_size != TW686X_AUDIO_PAGE_SZ || - rt->periods < TW686X_AUDIO_PERIODS_MIN || - rt->periods > TW686X_AUDIO_PERIODS_MAX) { - return -EINVAL; + if (dev->period_size != period_size) { + u32 reg; + + dev->period_size = period_size; + reg = reg_read(dev, AUDIO_CONTROL1); + reg &= ~(AUDIO_DMA_SIZE_MASK << AUDIO_DMA_SIZE_SHIFT); + reg |= period_size << AUDIO_DMA_SIZE_SHIFT; + + reg_write(dev, AUDIO_CONTROL1, reg); } + if (rt->periods < TW686X_AUDIO_PERIODS_MIN || + rt->periods > TW686X_AUDIO_PERIODS_MAX) + return -EINVAL; + spin_lock_irqsave(&ac->lock, flags); INIT_LIST_HEAD(&ac->buf_list); @@ -181,9 +207,19 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) ac->curr_bufs[0] = p_buf; ac->curr_bufs[1] = b_buf; ac->ptr = 0; + + if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) { + reg_write(dev, ADMA_P_ADDR[ac->ch], p_buf->dma); + reg_write(dev, ADMA_B_ADDR[ac->ch], b_buf->dma); + } + spin_unlock_irqrestore(&ac->lock, flags); return 0; + +err_audio_busy: + spin_unlock_irqrestore(&dev->lock, flags); + return -EBUSY; } static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) @@ -197,6 +233,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) case SNDRV_PCM_TRIGGER_START: if (ac->curr_bufs[0] && ac->curr_bufs[1]) { spin_lock_irqsave(&dev->lock, flags); + dev->audio_enabled = 1; tw686x_enable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); spin_unlock_irqrestore(&dev->lock, flags); @@ -209,6 +246,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) break; case SNDRV_PCM_TRIGGER_STOP: spin_lock_irqsave(&dev->lock, flags); + dev->audio_enabled = 0; tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); spin_unlock_irqrestore(&dev->lock, flags); @@ -266,8 +304,8 @@ static int tw686x_snd_pcm_init(struct tw686x_dev *dev) return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(dev->pci_dev), - TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, - TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ); + TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, + TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX); } static void tw686x_audio_dma_free(struct tw686x_dev *dev, @@ -290,11 +328,19 @@ static int tw686x_audio_dma_alloc(struct tw686x_dev *dev, { int pb; + /* + * In the memcpy DMA mode we allocate a consistent buffer + * and use it for the DMA capture. Otherwise, DMA + * acts on the ALSA buffers as received in pcm_prepare. + */ + if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) + return 0; + for (pb = 0; pb < 2; pb++) { u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch]; void *virt; - virt = pci_alloc_consistent(dev->pci_dev, TW686X_AUDIO_PAGE_SZ, + virt = pci_alloc_consistent(dev->pci_dev, AUDIO_DMA_SIZE_MAX, &ac->dma_descs[pb].phys); if (!virt) { dev_err(&dev->pci_dev->dev, @@ -303,7 +349,7 @@ static int tw686x_audio_dma_alloc(struct tw686x_dev *dev, return -ENOMEM; } ac->dma_descs[pb].virt = virt; - ac->dma_descs[pb].size = TW686X_AUDIO_PAGE_SZ; + ac->dma_descs[pb].size = AUDIO_DMA_SIZE_MAX; reg_write(dev, reg, ac->dma_descs[pb].phys); } return 0; @@ -334,12 +380,8 @@ int tw686x_audio_init(struct tw686x_dev *dev) struct snd_card *card; int err, ch; - /* - * AUDIO_CONTROL1 - * DMA byte length [31:19] = 4096 (i.e. ALSA period) - * External audio enable [0] = enabled - */ - reg_write(dev, AUDIO_CONTROL1, 0x80000001); + /* Enable external audio */ + reg_write(dev, AUDIO_CONTROL1, BIT(0)); err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index cf53b0e97be2..71a0453b1af1 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -21,12 +21,14 @@ * under stress testings it has been found that the machine can * freeze completely if DMA registers are programmed while streaming * is active. - * This driver tries to access hardware registers as infrequently - * as possible by: - * i. allocating fixed DMA buffers and memcpy'ing into - * vmalloc'ed buffers - * ii. using a timer to mitigate the rate of DMA reset operations, - * on DMA channels error. + * + * Therefore, driver implements a dma_mode called 'memcpy' which + * avoids cycling the DMA buffers, and insteads allocates extra DMA buffers + * and then copies into vmalloc'ed user buffers. + * + * In addition to this, when streaming is on, the driver tries to access + * hardware registers as infrequently as possible. This is done by using + * a timer to limit the rate at which DMA is reset on DMA channels error. */ #include @@ -55,6 +57,42 @@ static u32 dma_interval = 0x00098968; module_param(dma_interval, int, 0444); MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host"); +static unsigned int dma_mode = TW686X_DMA_MODE_MEMCPY; +static const char *dma_mode_name(unsigned int mode) +{ + switch (mode) { + case TW686X_DMA_MODE_MEMCPY: + return "memcpy"; + case TW686X_DMA_MODE_CONTIG: + return "contig"; + case TW686X_DMA_MODE_SG: + return "sg"; + default: + return "unknown"; + } +} + +static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp) +{ + return sprintf(buffer, dma_mode_name(dma_mode)); +} + +static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp) +{ + if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY))) + dma_mode = TW686X_DMA_MODE_MEMCPY; + else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_CONTIG))) + dma_mode = TW686X_DMA_MODE_CONTIG; + else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_SG))) + dma_mode = TW686X_DMA_MODE_SG; + else + return -EINVAL; + return 0; +} +module_param_call(dma_mode, tw686x_dma_mode_set, tw686x_dma_mode_get, + &dma_mode, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(dma_mode, "DMA operation mode (memcpy/contig/sg, default=memcpy)"); + void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel) { u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); @@ -212,6 +250,7 @@ static int tw686x_probe(struct pci_dev *pci_dev, if (!dev) return -ENOMEM; dev->type = pci_id->driver_data; + dev->dma_mode = dma_mode; sprintf(dev->name, "tw%04X", pci_dev->device); dev->video_channels = kcalloc(max_channels(dev), @@ -228,9 +267,10 @@ static int tw686x_probe(struct pci_dev *pci_dev, goto free_video; } - pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, + pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx (%s mode)\n", dev->name, pci_name(pci_dev), pci_dev->irq, - (unsigned long)pci_resource_start(pci_dev, 0)); + (unsigned long)pci_resource_start(pci_dev, 0), + dma_mode_name(dma_mode)); dev->pci_dev = pci_dev; if (pci_enable_device(pci_dev)) { diff --git a/drivers/media/pci/tw686x/tw686x-regs.h b/drivers/media/pci/tw686x/tw686x-regs.h index fcef586a4c8c..15a956642ef4 100644 --- a/drivers/media/pci/tw686x/tw686x-regs.h +++ b/drivers/media/pci/tw686x/tw686x-regs.h @@ -105,6 +105,10 @@ 0x2d0, 0x2d1, 0x2d2, 0x2d3 }) #define SYS_MODE_DMA_SHIFT 13 +#define AUDIO_DMA_SIZE_SHIFT 19 +#define AUDIO_DMA_SIZE_MIN SZ_512 +#define AUDIO_DMA_SIZE_MAX SZ_4K +#define AUDIO_DMA_SIZE_MASK (SZ_8K - 1) #define DMA_CMD_ENABLE BIT(31) #define INT_STATUS_DMA_TOUT BIT(17) @@ -119,4 +123,9 @@ #define TW686X_STD_PAL_CN 5 #define TW686X_STD_PAL_60 6 +#define TW686X_FIELD_MODE 0x3 +#define TW686X_FRAME_MODE 0x2 +/* 0x1 is reserved */ +#define TW686X_SG_MODE 0x0 + #define TW686X_FIFO_ERROR(x) (x & ~(0xff)) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 253e10823ba3..cdb16de770fe 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include "tw686x.h" #include "tw686x-regs.h" @@ -26,6 +28,11 @@ #define TW686X_INPUTS_PER_CH 4 #define TW686X_VIDEO_WIDTH 720 #define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576) +#define TW686X_MAX_FPS(id) ((id & V4L2_STD_525_60) ? 30 : 25) + +#define TW686X_MAX_SG_ENTRY_SIZE 4096 +#define TW686X_MAX_SG_DESC_COUNT 256 /* PAL 720x576 needs 203 4-KB pages */ +#define TW686X_SG_TABLE_SIZE (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc)) static const struct tw686x_format formats[] = { { @@ -43,53 +50,367 @@ static const struct tw686x_format formats[] = { } }; -static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps) +static void tw686x_buf_done(struct tw686x_video_channel *vc, + unsigned int pb) { - static const unsigned int map[15] = { - 0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041, - 0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445, - 0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555 - }; + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + struct vb2_v4l2_buffer *vb; + struct vb2_buffer *vb2_buf; - static const unsigned int std_625_50[26] = { - 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, - 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0 - }; + if (vc->curr_bufs[pb]) { + vb = &vc->curr_bufs[pb]->vb; - static const unsigned int std_525_60[31] = { - 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, - 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0 - }; + vb->field = dev->dma_ops->field; + vb->sequence = vc->sequence++; + vb2_buf = &vb->vb2_buf; - unsigned int i; + if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) + memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, + desc->size); + vb2_buf->timestamp = ktime_get_ns(); + vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); + } + + vc->pb = !pb; +} + +/* + * We can call this even when alloc_dma failed for the given channel + */ +static void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + struct pci_dev *pci_dev; + unsigned long flags; + + /* Check device presence. Shouldn't really happen! */ + spin_lock_irqsave(&dev->lock, flags); + pci_dev = dev->pci_dev; + spin_unlock_irqrestore(&dev->lock, flags); + if (!pci_dev) { + WARN(1, "trying to deallocate on missing device\n"); + return; + } + + if (desc->virt) { + pci_free_consistent(dev->pci_dev, desc->size, + desc->virt, desc->phys); + desc->virt = NULL; + } +} + +static int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dev *dev = vc->dev; + u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; + unsigned int len; + void *virt; + + WARN(vc->dma_descs[pb].virt, + "Allocating buffer but previous still here\n"); + + len = (vc->width * vc->height * vc->format->depth) >> 3; + virt = pci_alloc_consistent(dev->pci_dev, len, + &vc->dma_descs[pb].phys); + if (!virt) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to allocate %s-buffer\n", + vc->ch, pb ? "B" : "P"); + return -ENOMEM; + } + vc->dma_descs[pb].size = len; + vc->dma_descs[pb].virt = virt; + reg_write(dev, reg, vc->dma_descs[pb].phys); + + return 0; +} + +static void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_v4l2_buf *buf; + + while (!list_empty(&vc->vidq_queued)) { + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + + vc->curr_bufs[pb] = buf; + return; + } + vc->curr_bufs[pb] = NULL; +} - if (std & V4L2_STD_525_60) { - if (fps >= ARRAY_SIZE(std_525_60)) - fps = 30; - i = std_525_60[fps]; +static const struct tw686x_dma_ops memcpy_dma_ops = { + .alloc = tw686x_memcpy_dma_alloc, + .free = tw686x_memcpy_dma_free, + .buf_refill = tw686x_memcpy_buf_refill, + .mem_ops = &vb2_vmalloc_memops, + .hw_dma_mode = TW686X_FRAME_MODE, + .field = V4L2_FIELD_INTERLACED, +}; + +static void tw686x_contig_buf_refill(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_v4l2_buf *buf; + + while (!list_empty(&vc->vidq_queued)) { + u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; + dma_addr_t phys; + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + + phys = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + reg_write(vc->dev, reg, phys); + + buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; + vc->curr_bufs[pb] = buf; + return; + } + vc->curr_bufs[pb] = NULL; +} + +static const struct tw686x_dma_ops contig_dma_ops = { + .buf_refill = tw686x_contig_buf_refill, + .mem_ops = &vb2_dma_contig_memops, + .hw_dma_mode = TW686X_FRAME_MODE, + .field = V4L2_FIELD_INTERLACED, +}; + +static int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs, + struct tw686x_v4l2_buf *buf, + unsigned int buf_len) +{ + struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); + unsigned int len, entry_len; + struct scatterlist *sg; + int i, count; + + /* Clear the scatter-gather table */ + memset(descs, 0, TW686X_SG_TABLE_SIZE); + + count = 0; + for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { + dma_addr_t phys = sg_dma_address(sg); + len = sg_dma_len(sg); + + while (len && buf_len) { + + if (count == TW686X_MAX_SG_DESC_COUNT) + return -ENOMEM; + + entry_len = min_t(unsigned int, len, + TW686X_MAX_SG_ENTRY_SIZE); + entry_len = min_t(unsigned int, entry_len, buf_len); + descs[count].phys = cpu_to_le32(phys); + descs[count++].flags_length = + cpu_to_le32(BIT(30) | entry_len); + phys += entry_len; + len -= entry_len; + buf_len -= entry_len; + } + + if (!buf_len) + return 0; + } + + return -ENOMEM; +} + +static void tw686x_sg_buf_refill(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dev *dev = vc->dev; + struct tw686x_v4l2_buf *buf; + + while (!list_empty(&vc->vidq_queued)) { + unsigned int buf_len; + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + + buf_len = (vc->width * vc->height * vc->format->depth) >> 3; + if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to fill %s-buffer\n", + vc->ch, pb ? "B" : "P"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + continue; + } + + buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; + vc->curr_bufs[pb] = buf; + return; + } + + vc->curr_bufs[pb] = NULL; +} + +static void tw686x_sg_dma_free(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + + if (desc->size) { + pci_free_consistent(dev->pci_dev, desc->size, + desc->virt, desc->phys); + desc->virt = NULL; + } + + vc->sg_descs[pb] = NULL; +} + +static int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] : + DMA_PAGE_TABLE0_ADDR[vc->ch]; + void *virt; + + if (desc->size) { + + virt = pci_alloc_consistent(dev->pci_dev, desc->size, + &desc->phys); + if (!virt) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to allocate %s-buffer\n", + vc->ch, pb ? "B" : "P"); + return -ENOMEM; + } + desc->virt = virt; + reg_write(dev, reg, desc->phys); } else { - if (fps >= ARRAY_SIZE(std_625_50)) - fps = 25; - i = std_625_50[fps]; + virt = dev->video_channels[0].dma_descs[pb].virt + + vc->ch * TW686X_SG_TABLE_SIZE; } - return map[i]; + vc->sg_descs[pb] = virt; + return 0; +} + +static int tw686x_sg_setup(struct tw686x_dev *dev) +{ + unsigned int sg_table_size, pb, ch, channels; + + if (is_second_gen(dev)) { + /* + * TW6865/TW6869: each channel needs a pair of + * P-B descriptor tables. + */ + channels = max_channels(dev); + sg_table_size = TW686X_SG_TABLE_SIZE; + } else { + /* + * TW6864/TW6868: we need to allocate a pair of + * P-B descriptor tables, common for all channels. + * Each table will be bigger than 4 KB. + */ + channels = 1; + sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE; + } + + for (ch = 0; ch < channels; ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + for (pb = 0; pb < 2; pb++) + vc->dma_descs[pb].size = sg_table_size; + } + + return 0; +} + +static const struct tw686x_dma_ops sg_dma_ops = { + .setup = tw686x_sg_setup, + .alloc = tw686x_sg_dma_alloc, + .free = tw686x_sg_dma_free, + .buf_refill = tw686x_sg_buf_refill, + .mem_ops = &vb2_dma_sg_memops, + .hw_dma_mode = TW686X_SG_MODE, + .field = V4L2_FIELD_SEQ_TB, +}; + +static const unsigned int fps_map[15] = { + /* + * bit 31 enables selecting the field control register + * bits 0-29 are a bitmask with fields that will be output. + * For NTSC (and PAL-M, PAL-60), all 30 bits are used. + * For other PAL standards, only the first 25 bits are used. + */ + 0x00000000, /* output all fields */ + 0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */ + 0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */ + 0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */ + 0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */ + 0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */ + 0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */ + 0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */ + 0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */ + 0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */ + 0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */ + 0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */ + 0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */ + 0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */ + 0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */ +}; + +static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps) +{ + unsigned long mask; + + if (!index || index >= ARRAY_SIZE(fps_map)) + return max_fps; + + mask = GENMASK(max_fps - 1, 0); + return hweight_long(fps_map[index] & mask); +} + +static unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps) +{ + unsigned int idx, real_fps; + int delta; + + /* First guess */ + idx = (12 + 15 * fps) / max_fps; + + /* Minimal possible framerate is 2 frames per second */ + if (!idx) + return 1; + + /* Check if the difference is bigger than abs(1) and adjust */ + real_fps = tw686x_real_fps(idx, max_fps); + delta = real_fps - fps; + if (delta < -1) + idx++; + else if (delta > 1) + idx--; + + /* Max framerate */ + if (idx >= 15) + return 0; + + return idx; } static void tw686x_set_framerate(struct tw686x_video_channel *vc, unsigned int fps) { - unsigned int map; - - if (vc->fps == fps) - return; + unsigned int i; - map = tw686x_fields_map(vc->video_standard, fps) << 1; - map |= map << 1; - if (map > 0) - map |= BIT(31); - reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map); - vc->fps = fps; + i = tw686x_fps_idx(fps, TW686X_MAX_FPS(vc->video_standard)); + reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]); + vc->fps = tw686x_real_fps(i, TW686X_MAX_FPS(vc->video_standard)); } static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) @@ -104,7 +425,7 @@ static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); unsigned int szimage = @@ -152,75 +473,6 @@ static void tw686x_buf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&vc->qlock, flags); } -/* - * We can call this even when alloc_dma failed for the given channel - */ -static void tw686x_free_dma(struct tw686x_video_channel *vc, unsigned int pb) -{ - struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; - struct tw686x_dev *dev = vc->dev; - struct pci_dev *pci_dev; - unsigned long flags; - - /* Check device presence. Shouldn't really happen! */ - spin_lock_irqsave(&dev->lock, flags); - pci_dev = dev->pci_dev; - spin_unlock_irqrestore(&dev->lock, flags); - if (!pci_dev) { - WARN(1, "trying to deallocate on missing device\n"); - return; - } - - if (desc->virt) { - pci_free_consistent(dev->pci_dev, desc->size, - desc->virt, desc->phys); - desc->virt = NULL; - } -} - -static int tw686x_alloc_dma(struct tw686x_video_channel *vc, unsigned int pb) -{ - struct tw686x_dev *dev = vc->dev; - u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; - unsigned int len; - void *virt; - - WARN(vc->dma_descs[pb].virt, - "Allocating buffer but previous still here\n"); - - len = (vc->width * vc->height * vc->format->depth) >> 3; - virt = pci_alloc_consistent(dev->pci_dev, len, - &vc->dma_descs[pb].phys); - if (!virt) { - v4l2_err(&dev->v4l2_dev, - "dma%d: unable to allocate %s-buffer\n", - vc->ch, pb ? "B" : "P"); - return -ENOMEM; - } - vc->dma_descs[pb].size = len; - vc->dma_descs[pb].virt = virt; - reg_write(dev, reg, vc->dma_descs[pb].phys); - - return 0; -} - -static void tw686x_buffer_refill(struct tw686x_video_channel *vc, - unsigned int pb) -{ - struct tw686x_v4l2_buf *buf; - - while (!list_empty(&vc->vidq_queued)) { - - buf = list_first_entry(&vc->vidq_queued, - struct tw686x_v4l2_buf, list); - list_del(&buf->list); - - vc->curr_bufs[pb] = buf; - return; - } - vc->curr_bufs[pb] = NULL; -} - static void tw686x_clear_queue(struct tw686x_video_channel *vc, enum vb2_buffer_state state) { @@ -262,7 +514,8 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) spin_lock_irqsave(&vc->qlock, flags); /* Sanity check */ - if (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt) { + if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY && + (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) { spin_unlock_irqrestore(&vc->qlock, flags); v4l2_err(&dev->v4l2_dev, "video%d: refusing to start without DMA buffers\n", @@ -272,7 +525,7 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) } for (pb = 0; pb < 2; pb++) - tw686x_buffer_refill(vc, pb); + dev->dma_ops->buf_refill(vc, pb); spin_unlock_irqrestore(&vc->qlock, flags); vc->sequence = 0; @@ -375,10 +628,11 @@ static int tw686x_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; f->fmt.pix.width = vc->width; f->fmt.pix.height = vc->height; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.field = dev->dma_ops->field; f->fmt.pix.pixelformat = vc->format->fourcc; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8; @@ -390,6 +644,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard); const struct tw686x_format *format; @@ -412,7 +667,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.field = dev->dma_ops->field; return 0; } @@ -421,6 +676,7 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; u32 val, width, line_width, height; unsigned long bitsperframe; int err, pb; @@ -438,15 +694,16 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, vc->height = f->fmt.pix.height; /* We need new DMA buffers if the framesize has changed */ - if (bitsperframe != vc->width * vc->height * vc->format->depth) { + if (dev->dma_ops->alloc && + bitsperframe != vc->width * vc->height * vc->format->depth) { for (pb = 0; pb < 2; pb++) - tw686x_free_dma(vc, pb); + dev->dma_ops->free(vc, pb); for (pb = 0; pb < 2; pb++) { - err = tw686x_alloc_dma(vc, pb); + err = dev->dma_ops->alloc(vc, pb); if (err) { if (pb > 0) - tw686x_free_dma(vc, 0); + dev->dma_ops->free(vc, 0); return err; } } @@ -464,6 +721,19 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, else val &= ~BIT(24); + val &= ~0x7ffff; + + /* Program the DMA scatter-gather */ + if (dev->dma_mode == TW686X_DMA_MODE_SG) { + u32 start_idx, end_idx; + + start_idx = is_second_gen(dev) ? + 0 : vc->ch * TW686X_MAX_SG_DESC_COUNT; + end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1; + + val |= (end_idx << 10) | start_idx; + } + val &= ~(0x7 << 20); val |= vc->format->mode << 20; reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); @@ -540,6 +810,12 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) ret = tw686x_g_fmt_vid_cap(file, priv, &f); if (!ret) tw686x_s_fmt_vid_cap(file, priv, &f); + + /* + * Frame decimation depends on the chosen standard, + * so reset it to the current value. + */ + tw686x_set_framerate(vc, vc->fps); return 0; } @@ -609,6 +885,40 @@ static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } +static int tw686x_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct v4l2_captureparm *cp = &sp->parm.capture; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + sp->parm.capture.readbuffers = 3; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = vc->fps; + return 0; +} + +static int tw686x_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct v4l2_captureparm *cp = &sp->parm.capture; + unsigned int denominator = cp->timeperframe.denominator; + unsigned int numerator = cp->timeperframe.numerator; + unsigned int fps; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + fps = (!numerator || !denominator) ? 0 : denominator / numerator; + if (vc->fps != fps) + tw686x_set_framerate(vc, fps); + return tw686x_g_parm(file, priv, sp); +} + static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -695,6 +1005,9 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_g_std = tw686x_g_std, .vidioc_s_std = tw686x_s_std, + .vidioc_g_parm = tw686x_g_parm, + .vidioc_s_parm = tw686x_s_parm, + .vidioc_enum_input = tw686x_enum_input, .vidioc_g_input = tw686x_g_input, .vidioc_s_input = tw686x_s_input, @@ -713,26 +1026,11 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static void tw686x_buffer_copy(struct tw686x_video_channel *vc, - unsigned int pb, struct vb2_v4l2_buffer *vb) -{ - struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; - struct vb2_buffer *vb2_buf = &vb->vb2_buf; - - vb->field = V4L2_FIELD_INTERLACED; - vb->sequence = vc->sequence++; - - memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, desc->size); - vb2_buf->timestamp = ktime_get_ns(); - vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); -} - void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, unsigned int pb_status, unsigned int fifo_status, unsigned int *reset_ch) { struct tw686x_video_channel *vc; - struct vb2_v4l2_buffer *vb; unsigned long flags; unsigned int ch, pb; @@ -781,14 +1079,9 @@ void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, continue; } - /* handle video stream */ spin_lock_irqsave(&vc->qlock, flags); - if (vc->curr_bufs[pb]) { - vb = &vc->curr_bufs[pb]->vb; - tw686x_buffer_copy(vc, pb, vb); - } - vc->pb = !pb; - tw686x_buffer_refill(vc, pb); + tw686x_buf_done(vc, pb); + dev->dma_ops->buf_refill(vc, pb); spin_unlock_irqrestore(&vc->qlock, flags); } } @@ -803,8 +1096,9 @@ void tw686x_video_free(struct tw686x_dev *dev) if (vc->device) video_unregister_device(vc->device); - for (pb = 0; pb < 2; pb++) - tw686x_free_dma(vc, pb); + if (dev->dma_ops->free) + for (pb = 0; pb < 2; pb++) + dev->dma_ops->free(vc, pb); } } @@ -813,10 +1107,25 @@ int tw686x_video_init(struct tw686x_dev *dev) unsigned int ch, val, pb; int err; + if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) + dev->dma_ops = &memcpy_dma_ops; + else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG) + dev->dma_ops = &contig_dma_ops; + else if (dev->dma_mode == TW686X_DMA_MODE_SG) + dev->dma_ops = &sg_dma_ops; + else + return -EINVAL; + err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); if (err) return err; + if (dev->dma_ops->setup) { + err = dev->dma_ops->setup(dev); + if (err) + return err; + } + for (ch = 0; ch < max_channels(dev); ch++) { struct tw686x_video_channel *vc = &dev->video_channels[ch]; struct video_device *vdev; @@ -842,10 +1151,12 @@ int tw686x_video_init(struct tw686x_dev *dev) reg_write(dev, HACTIVE_LO[ch], 0xd0); reg_write(dev, VIDEO_SIZE[ch], 0); - for (pb = 0; pb < 2; pb++) { - err = tw686x_alloc_dma(vc, pb); - if (err) - goto error; + if (dev->dma_ops->alloc) { + for (pb = 0; pb < 2; pb++) { + err = dev->dma_ops->alloc(vc, pb); + if (err) + goto error; + } } vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; @@ -853,11 +1164,12 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->vidq.drv_priv = vc; vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf); vc->vidq.ops = &tw686x_video_qops; - vc->vidq.mem_ops = &vb2_vmalloc_memops; + vc->vidq.mem_ops = dev->dma_ops->mem_ops; vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vc->vidq.min_buffers_needed = 2; vc->vidq.lock = &vc->vb_mutex; vc->vidq.gfp_flags = GFP_DMA32; + vc->vidq.dev = &dev->pci_dev->dev; err = vb2_queue_init(&vc->vidq); if (err) { @@ -915,10 +1227,9 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->num = vdev->num; } - /* Set DMA frame mode on all channels. Only supported mode for now. */ val = TW686X_DEF_PHASE_REF; for (ch = 0; ch < max_channels(dev); ch++) - val |= TW686X_FRAME_MODE << (16 + ch * 2); + val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2); reg_write(dev, PHASE_REF, val); reg_write(dev, MISC2[0], 0xe7); diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h index 44b5755acf02..f24a2a9bcdb2 100644 --- a/drivers/media/pci/tw686x/tw686x.h +++ b/drivers/media/pci/tw686x/tw686x.h @@ -27,16 +27,14 @@ #define TYPE_SECOND_GEN 0x10 #define TW686X_DEF_PHASE_REF 0x1518 -#define TW686X_FIELD_MODE 0x3 -#define TW686X_FRAME_MODE 0x2 -/* 0x1 is reserved */ -#define TW686X_SG_MODE 0x0 - -#define TW686X_AUDIO_PAGE_SZ 4096 #define TW686X_AUDIO_PAGE_MAX 16 #define TW686X_AUDIO_PERIODS_MIN 2 #define TW686X_AUDIO_PERIODS_MAX TW686X_AUDIO_PAGE_MAX +#define TW686X_DMA_MODE_MEMCPY 0 +#define TW686X_DMA_MODE_CONTIG 1 +#define TW686X_DMA_MODE_SG 2 + struct tw686x_format { char *name; unsigned int fourcc; @@ -50,6 +48,12 @@ struct tw686x_dma_desc { unsigned int size; }; +struct tw686x_sg_desc { + /* 3 MSBits for flags, 13 LSBits for length */ + __le32 flags_length; + __le32 phys; +}; + struct tw686x_audio_buf { dma_addr_t dma; void *virt; @@ -82,6 +86,7 @@ struct tw686x_video_channel { struct video_device *device; struct tw686x_v4l2_buf *curr_bufs[2]; struct tw686x_dma_desc dma_descs[2]; + struct tw686x_sg_desc *sg_descs[2]; struct v4l2_ctrl_handler ctrl_handler; const struct tw686x_format *format; @@ -99,6 +104,16 @@ struct tw686x_video_channel { bool no_signal; }; +struct tw686x_dma_ops { + int (*setup)(struct tw686x_dev *dev); + int (*alloc)(struct tw686x_video_channel *vc, unsigned int pb); + void (*free)(struct tw686x_video_channel *vc, unsigned int pb); + void (*buf_refill)(struct tw686x_video_channel *vc, unsigned int pb); + const struct vb2_mem_ops *mem_ops; + enum v4l2_field field; + u32 hw_dma_mode; +}; + /** * struct tw686x_dev - global device status * @lock: spinlock controlling access to the @@ -112,15 +127,18 @@ struct tw686x_dev { char name[32]; unsigned int type; + unsigned int dma_mode; struct pci_dev *pci_dev; __u32 __iomem *mmio; - void *alloc_ctx; - + const struct tw686x_dma_ops *dma_ops; struct tw686x_video_channel *video_channels; struct tw686x_audio_channel *audio_channels; - int audio_rate; /* per-device value */ + /* Per-device audio parameters */ + int audio_rate; + int period_size; + int audio_enabled; struct timer_list dma_delay_timer; u32 pending_dma_en; /* must be protected by lock */ @@ -143,6 +161,12 @@ static inline unsigned int max_channels(struct tw686x_dev *dev) return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ } +static inline unsigned is_second_gen(struct tw686x_dev *dev) +{ + /* each channel has its own DMA SG table */ + return dev->type & TYPE_SECOND_GEN; +} + void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel); void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel); diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c index b87ddba8608f..c12ca9f96bac 100644 --- a/drivers/media/pci/zoran/zr36016.c +++ b/drivers/media/pci/zoran/zr36016.c @@ -246,10 +246,6 @@ static int zr36016_pushit (struct zr36016 *ptr, //TODO// ========================================================================= */ -// needed offset values PAL NTSC SECAM -static const int zr016_xoff[] = { 20, 20, 20 }; -static const int zr016_yoff[] = { 8, 9, 7 }; - static void zr36016_init (struct zr36016 *ptr) { diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 84e041c0a70e..f25344bc7912 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -110,6 +110,7 @@ source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" +source "drivers/media/platform/rcar-vin/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" @@ -152,6 +153,36 @@ config VIDEO_CODA Coda is a range of video codec IPs that supports H.264, MPEG-4, and other video formats. +config VIDEO_MEDIATEK_VPU + tristate "Mediatek Video Processor Unit" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MEDIATEK || COMPILE_TEST + ---help--- + This driver provides downloading VPU firmware and + communicating with VPU. This driver for hw video + codec embedded in Mediatek's MT8173 SOCs. It is able + to handle video decoding/encoding in a range of formats. + + To compile this driver as a module, choose M here: the + module will be called mtk-vpu. + +config VIDEO_MEDIATEK_VCODEC + tristate "Mediatek Video Codec driver" + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MEDIATEK || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_MEDIATEK_VPU + default n + ---help--- + Mediatek video codec driver provides HW capability to + encode and decode in a range of video formats + This driver rely on VPU driver to communicate with VPU. + + To compile this driver as a module, choose M here: the + module will be called mtk-vcodec + config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE @@ -247,10 +278,24 @@ config VIDEO_RENESAS_JPU To compile this driver as a module, choose M here: the module will be called rcar_jpu. +config VIDEO_RENESAS_FCP + tristate "Renesas Frame Compression Processor" + depends on ARCH_RENESAS || COMPILE_TEST + depends on OF + ---help--- + This is a driver for the Renesas Frame Compression Processor (FCP). + The FCP is a companion module of video processing modules in the + Renesas R-Car Gen3 SoCs. It handles memory access for the codec, + VSP and FDP modules. + + To compile this driver as a module, choose M here: the module + will be called rcar-fcp. + config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on (ARCH_RENESAS && OF) || COMPILE_TEST + depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index bbb7bd1eb268..21771c1a13fb 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ @@ -55,4 +56,10 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ + ccflags-y += -I$(srctree)/drivers/media/i2c + +obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/ diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index e749eb7c3be9..b33b9e35e60e 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1901,21 +1901,20 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); unsigned size = vpfe->fmt.fmt.pix.sizeimage; if (vq->num_buffers + *nbuffers < 3) *nbuffers = 3 - vq->num_buffers; - alloc_ctxs[0] = vpfe->alloc_ctx; if (*nplanes) { if (sizes[0] < size) @@ -2364,13 +2363,6 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) goto probe_out; /* Initialize videobuf2 queue as per the buffer type */ - vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev); - if (IS_ERR(vpfe->alloc_ctx)) { - vpfe_err(vpfe, "Failed to get the context\n"); - err = PTR_ERR(vpfe->alloc_ctx); - goto probe_out; - } - q = &vpfe->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; @@ -2381,11 +2373,11 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &vpfe->lock; q->min_buffers_needed = 1; + q->dev = vpfe->pdev; err = vb2_queue_init(q); if (err) { vpfe_err(vpfe, "vb2_queue_init() failed\n"); - vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx); goto probe_out; } diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 777bf97fea57..17d7aa426788 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -264,8 +264,6 @@ struct vpfe_device { struct v4l2_rect crop; /* Buffer queue used in video-buf */ struct vb2_queue buffer_queue; - /* Allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* Queue of filled frames */ struct list_head dma_queue; /* IRQ lock for DMA queue */ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index d0092dae7a57..8eb03397d736 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -91,8 +91,6 @@ struct bcap_device { struct bcap_buffer *cur_frm; /* buffer queue used in videobuf2 */ struct vb2_queue buffer_queue; - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* queue of filled frames */ struct list_head dma_queue; /* used in videobuf2 callback */ @@ -203,13 +201,12 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) static int bcap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); if (vq->num_buffers + *nbuffers < 2) *nbuffers = 2; - alloc_ctxs[0] = bcap_dev->alloc_ctx; if (*nplanes) return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0; @@ -820,12 +817,6 @@ static int bcap_probe(struct platform_device *pdev) } bcap_dev->ppi->priv = bcap_dev; - bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(bcap_dev->alloc_ctx)) { - ret = PTR_ERR(bcap_dev->alloc_ctx); - goto err_free_ppi; - } - vfd = &bcap_dev->video_dev; /* initialize field of video device */ vfd->release = video_device_release_empty; @@ -839,7 +830,7 @@ static int bcap_probe(struct platform_device *pdev) if (ret) { v4l2_err(pdev->dev.driver, "Unable to register v4l2 device\n"); - goto err_cleanup_ctx; + goto err_free_ppi; } v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); @@ -863,6 +854,7 @@ static int bcap_probe(struct platform_device *pdev) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &bcap_dev->mutex; q->min_buffers_needed = 1; + q->dev = &pdev->dev; ret = vb2_queue_init(q); if (ret) @@ -967,8 +959,6 @@ static int bcap_probe(struct platform_device *pdev) v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); err_unreg_v4l2: v4l2_device_unregister(&bcap_dev->v4l2_dev); -err_cleanup_ctx: - vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); err_free_ppi: ppi_delete_instance(bcap_dev->ppi); err_free_dev: @@ -986,7 +976,6 @@ static int bcap_remove(struct platform_device *pdev) video_unregister_device(&bcap_dev->video_dev); v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); v4l2_device_unregister(v4l2_dev); - vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); ppi_delete_instance(bcap_dev->ppi); kfree(bcap_dev); return 0; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 133ab9f70f85..c39718a63e5e 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1139,7 +1139,7 @@ static void set_default_params(struct coda_ctx *ctx) */ static int coda_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct coda_ctx *ctx = vb2_get_drv_priv(vq); struct coda_q_data *q_data; @@ -1151,9 +1151,6 @@ static int coda_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - /* Set to vb2-dma-contig allocator context, ignored by vb2-vmalloc */ - alloc_ctxs[0] = ctx->dev->alloc_ctx; - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "get %d buffer(s) of size %d each.\n", *nbuffers, size); @@ -1599,6 +1596,7 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) * that videobuf2 will keep the value of bytesused intact. */ vq->allow_zero_bytesused = 1; + vq->dev = &ctx->dev->plat_dev->dev; return vb2_queue_init(vq); } @@ -2040,16 +2038,10 @@ static void coda_fw_callback(const struct firmware *fw, void *context) if (ret < 0) goto put_pm; - dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(dev->alloc_ctx)) { - v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n"); - goto put_pm; - } - dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); - goto rel_ctx; + goto put_pm; } for (i = 0; i < dev->devtype->num_vdevs; i++) { @@ -2072,8 +2064,6 @@ static void coda_fw_callback(const struct firmware *fw, void *context) while (--i >= 0) video_unregister_device(&dev->vfd[i]); v4l2_m2m_release(dev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); put_pm: pm_runtime_put_sync(&pdev->dev); } @@ -2226,7 +2216,7 @@ static int coda_probe(struct platform_device *pdev) dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); if (IS_ERR(dev->rstc)) { ret = PTR_ERR(dev->rstc); - if (ret == -ENOENT || ret == -ENOSYS) { + if (ret == -ENOENT || ret == -ENOTSUPP) { dev->rstc = NULL; } else { dev_err(&pdev->dev, "failed get reset control: %d\n", @@ -2324,8 +2314,6 @@ static int coda_remove(struct platform_device *pdev) if (dev->m2m_dev) v4l2_m2m_release(dev->m2m_dev); pm_runtime_disable(&pdev->dev); - if (dev->alloc_ctx) - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); destroy_workqueue(dev->workqueue); if (dev->iram.vaddr) diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 8f2c71e06966..53f96661683c 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -92,7 +92,6 @@ struct coda_dev { struct mutex coda_mutex; struct workqueue_struct *workqueue; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; struct list_head instances; unsigned long instance_mask; struct dentry *debugfs_root; diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/davinci/ccdc_hw_device.h index 86b9b3518965..ae5605de7679 100644 --- a/drivers/media/platform/davinci/ccdc_hw_device.h +++ b/drivers/media/platform/davinci/ccdc_hw_device.h @@ -80,13 +80,6 @@ struct ccdc_hw_ops { /* Pointer to function to get line length */ unsigned int (*get_line_length) (void); - /* Query CCDC control IDs */ - int (*queryctrl)(struct v4l2_queryctrl *qctrl); - /* Set CCDC control */ - int (*set_control)(struct v4l2_control *ctrl); - /* Get CCDC control */ - int (*get_control)(struct v4l2_control *ctrl); - /* Pointer to function to set frame buffer address */ void (*setfbaddr) (unsigned long addr); /* Pointer to function to get field id */ diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0abcdfe97a6c..0b1709e96673 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -230,7 +230,7 @@ static int vpbe_buffer_prepare(struct vb2_buffer *vb) static int vpbe_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { /* Get the file handle object and layer object */ @@ -242,7 +242,6 @@ vpbe_buffer_queue_setup(struct vb2_queue *vq, /* Store number of buffers allocated in numbuffer member */ if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS) *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers; - alloc_ctxs[0] = layer->alloc_ctx; if (*nplanes) return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0; @@ -1451,20 +1450,13 @@ static int vpbe_display_probe(struct platform_device *pdev) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &disp_dev->dev[i]->opslock; + q->dev = disp_dev->vpbe_dev->pdev; err = vb2_queue_init(q); if (err) { v4l2_err(v4l2_dev, "vb2_queue_init() failed\n"); goto probe_out; } - disp_dev->dev[i]->alloc_ctx = - vb2_dma_contig_init_ctx(disp_dev->vpbe_dev->pdev); - if (IS_ERR(disp_dev->dev[i]->alloc_ctx)) { - v4l2_err(v4l2_dev, "Failed to get the context\n"); - err = PTR_ERR(disp_dev->dev[i]->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue); if (register_device(disp_dev->dev[i], disp_dev, pdev)) { @@ -1482,7 +1474,6 @@ static int vpbe_display_probe(struct platform_device *pdev) for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { /* Unregister video device */ if (disp_dev->dev[k] != NULL) { - vb2_dma_contig_cleanup_ctx(disp_dev->dev[k]->alloc_ctx); video_unregister_device(&disp_dev->dev[k]->video_dev); kfree(disp_dev->dev[k]); } @@ -1510,7 +1501,6 @@ static int vpbe_display_remove(struct platform_device *pdev) for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the layer object */ vpbe_display_layer = disp_dev->dev[i]; - vb2_dma_contig_cleanup_ctx(vpbe_display_layer->alloc_ctx); /* Unregister video device */ video_unregister_device(&vpbe_display_layer->video_dev); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 08f7028c7560..5104cc0ee40e 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -107,14 +107,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -133,7 +133,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = common->alloc_ctx; /* Calculate the offset for Y and C data in the buffer */ vpif_calculate_offsets(ch); @@ -1371,6 +1370,7 @@ static int vpif_probe_complete(void) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &common->lock; + q->dev = vpif_dev; err = vb2_queue_init(q); if (err) { @@ -1378,13 +1378,6 @@ static int vpif_probe_complete(void) goto probe_out; } - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - err = PTR_ERR(common->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&common->dma_queue); /* Initialize the video_device structure */ @@ -1412,7 +1405,6 @@ static int vpif_probe_complete(void) /* Get the pointer to the channel object */ ch = vpif_obj.dev[k]; common = &ch->common[k]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(&ch->video_dev); } @@ -1546,7 +1538,6 @@ static int vpif_remove(struct platform_device *device) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 4a7600929b61..9e35b6771d22 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -65,8 +65,6 @@ struct common_obj { struct v4l2_format fmt; /* Buffer queue used in video-buf */ struct vb2_queue buffer_queue; - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* Queue of filled frames */ struct list_head dma_queue; /* Used in video-buf */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index f40755cf1bf2..75b27233ec2f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -102,14 +102,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -126,7 +126,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = common->alloc_ctx; /* Calculate the offset for Y and C data in the buffer */ vpif_calculate_offsets(ch); @@ -1191,19 +1190,13 @@ static int vpif_probe_complete(void) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &common->lock; + q->dev = vpif_dev; err = vb2_queue_init(q); if (err) { vpif_err("vpif_display: vb2_queue_init() failed\n"); goto probe_out; } - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - err = PTR_ERR(common->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&common->dma_queue); /* register video device */ @@ -1233,7 +1226,6 @@ static int vpif_probe_complete(void) for (k = 0; k < j; k++) { ch = vpif_obj.dev[k]; common = &ch->common[k]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); video_unregister_device(&ch->video_dev); } return err; @@ -1355,7 +1347,6 @@ static int vpif_remove(struct platform_device *device) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index e7a1723a1b7a..af2765fdcea8 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -74,8 +74,6 @@ struct common_obj { struct v4l2_format fmt; /* Used to store the format */ struct vb2_queue buffer_queue; /* Buffer queue used in * video-buf */ - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; struct list_head dma_queue; /* Queue of filled frames */ spinlock_t irqlock; /* Used in video-buf */ diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c04973669a47..787bd16c19e5 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1123,19 +1123,13 @@ static int gsc_probe(struct platform_device *pdev) if (ret < 0) goto err_m2m; - /* Initialize continious memory allocator */ - gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(gsc->alloc_ctx)) { - ret = PTR_ERR(gsc->alloc_ctx); - goto err_pm; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); pm_runtime_put(dev); return 0; -err_pm: - pm_runtime_put(dev); + err_m2m: gsc_unregister_m2m_device(gsc); err_v4l2: @@ -1152,7 +1146,7 @@ static int gsc_remove(struct platform_device *pdev) gsc_unregister_m2m_device(gsc); v4l2_device_unregister(&gsc->v4l2_dev); - vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); gsc_clk_put(gsc); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index ec4000c72172..7ad7b9dc2243 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -327,7 +327,6 @@ struct gsc_driverdata { * @irq_queue: interrupt handler waitqueue * @m2m: memory-to-memory V4L2 device information * @state: flags used to synchronize m2m and capture mode operation - * @alloc_ctx: videobuf2 memory allocator context * @vdev: video device for G-Scaler instance */ struct gsc_dev { @@ -341,7 +340,6 @@ struct gsc_dev { wait_queue_head_t irq_queue; struct gsc_m2m_device m2m; unsigned long state; - struct vb2_alloc_ctx *alloc_ctx; struct video_device vdev; struct v4l2_device v4l2_dev; }; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index a600e32e2543..ec6494cbdd45 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -213,7 +213,7 @@ static void gsc_m2m_device_run(void *priv) static int gsc_m2m_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct gsc_ctx *ctx = vb2_get_drv_priv(vq); struct gsc_frame *frame; @@ -227,10 +227,8 @@ static int gsc_m2m_queue_setup(struct vb2_queue *vq, return -EINVAL; *num_planes = frame->fmt->num_planes; - for (i = 0; i < frame->fmt->num_planes; i++) { + for (i = 0; i < frame->fmt->num_planes; i++) sizes[i] = frame->payload[i]; - allocators[i] = ctx->gsc_dev->alloc_ctx; - } return 0; } @@ -591,6 +589,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->gsc_dev->lock; + src_vq->dev = &ctx->gsc_dev->pdev->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -605,6 +604,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->gsc_dev->lock; + dst_vq->dev = &ctx->gsc_dev->pdev->dev; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index bf47d3b9cbe7..fdec499fbbda 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -340,7 +340,7 @@ int fimc_capture_resume(struct fimc_dev *fimc) static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_ctx *ctx = vq->drv_priv; struct fimc_frame *frame = &ctx->d_frame; @@ -354,11 +354,9 @@ static int queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = ctx->fimc_dev->alloc_ctx; - } return 0; } @@ -371,8 +369,6 @@ static int queue_setup(struct vb2_queue *vq, sizes[i] = frame->payload[i]; else sizes[i] = max_t(u32, size, frame->payload[i]); - - allocators[i] = ctx->fimc_dev->alloc_ctx; } return 0; @@ -1779,6 +1775,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q->buf_struct_size = sizeof(struct fimc_vid_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &fimc->lock; + q->dev = &fimc->pdev->dev; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index b1c1cea82a27..8f89ca21b631 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1018,19 +1018,11 @@ static int fimc_probe(struct platform_device *pdev) goto err_sd; } - /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(fimc->alloc_ctx)) { - ret = PTR_ERR(fimc->alloc_ctx); - goto err_gclk; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); return 0; -err_gclk: - if (!pm_runtime_enabled(dev)) - clk_disable(fimc->clock[CLK_GATE]); err_sd: fimc_unregister_capture_subdev(fimc); err_sclk: @@ -1123,7 +1115,7 @@ static int fimc_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); fimc_unregister_capture_subdev(fimc); - vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 6b7435453d2a..5615fefbf7af 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -307,7 +307,6 @@ struct fimc_m2m_device { */ struct fimc_vid_cap { struct fimc_ctx *ctx; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct exynos_video_entity ve; struct media_pad vd_pad; @@ -417,7 +416,6 @@ struct fimc_ctx; * @m2m: memory-to-memory V4L2 device information * @vid_cap: camera capture device information * @state: flags used to synchronize m2m and capture mode operation - * @alloc_ctx: videobuf2 memory allocator context * @pipeline: fimc video capture pipeline data structure */ struct fimc_dev { @@ -436,7 +434,6 @@ struct fimc_dev { struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; - struct vb2_alloc_ctx *alloc_ctx; }; /** diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 979c388ebf60..32ca55f16677 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -204,9 +204,6 @@ static int fimc_is_register_subdevs(struct fimc_is *is) if (ret < 0) return ret; - /* Initialize memory allocator context for the ISP DMA. */ - is->isp.alloc_ctx = is->alloc_ctx; - for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { for_each_available_child_of_node(i2c_bus, child) { ret = fimc_is_parse_sensor_config(is, index, child); @@ -847,18 +844,14 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; - is->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(is->alloc_ctx)) { - ret = PTR_ERR(is->alloc_ctx); - goto err_pm; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes * will be created within the subdev's registered() callback. */ ret = fimc_is_register_subdevs(is); if (ret < 0) - goto err_vb; + goto err_pm; ret = fimc_is_debugfs_create(is); if (ret < 0) @@ -877,8 +870,6 @@ static int fimc_is_probe(struct platform_device *pdev) fimc_is_debugfs_remove(is); err_sd: fimc_is_unregister_subdevs(is); -err_vb: - vb2_dma_contig_cleanup_ctx(is->alloc_ctx); err_pm: if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); @@ -939,7 +930,7 @@ static int fimc_is_remove(struct platform_device *pdev) fimc_is_runtime_suspend(dev); free_irq(is->irq, is); fimc_is_unregister_subdevs(is); - vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); fimc_is_debugfs_remove(is); release_firmware(is->fw.f_w); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 386eb49ece7e..3a82c6a214c7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -233,7 +233,6 @@ struct chain_config { * @pdev: pointer to FIMC-IS platform device * @pctrl: pointer to pinctrl structure for this device * @v4l2_dev: pointer to top the level v4l2_device - * @alloc_ctx: videobuf2 memory allocator context * @lock: mutex serializing video device and the subdev operations * @slock: spinlock protecting this data structure and the hw registers * @clocks: FIMC-LITE gate clock @@ -256,7 +255,6 @@ struct fimc_is { struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM]; struct fimc_is_setfile setfile; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_ctrl_handler ctrl_handler; struct mutex lock; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index c0816728cbfe..400ce0cb0c0d 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -40,7 +40,7 @@ static int isp_video_capture_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_isp *isp = vb2_get_drv_priv(vq); struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; @@ -57,20 +57,16 @@ static int isp_video_capture_queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = isp->alloc_ctx; - } return 0; } *num_planes = fmt->memplanes; - for (i = 0; i < fmt->memplanes; i++) { + for (i = 0; i < fmt->memplanes; i++) sizes[i] = (wh * fmt->depth[i]) / 8; - allocators[i] = isp->alloc_ctx; - } return 0; } @@ -597,6 +593,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, q->drv_priv = isp; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &isp->video_lock; + q->dev = &isp->pdev->dev; ret = vb2_queue_init(q); if (ret < 0) diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index e0686b5f1bf8..3cdd52491294 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -148,7 +148,6 @@ struct fimc_is_video { /** * struct fimc_isp - FIMC-IS ISP data structure * @pdev: pointer to FIMC-IS platform device - * @alloc_ctx: videobuf2 memory allocator context * @subdev: ISP v4l2_subdev * @subdev_pads: the ISP subdev media pads * @test_pattern: test pattern controls @@ -161,7 +160,6 @@ struct fimc_is_video { */ struct fimc_isp { struct platform_device *pdev; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; struct v4l2_mbus_framefmt src_fmt; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index dc1b929f7a33..a0f149fb88e1 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -357,7 +357,7 @@ static void stop_streaming(struct vb2_queue *q) static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_lite *fimc = vq->drv_priv; struct flite_frame *frame = &fimc->out_frame; @@ -371,20 +371,16 @@ static int queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = fimc->alloc_ctx; - } return 0; } *num_planes = fmt->memplanes; - for (i = 0; i < fmt->memplanes; i++) { + for (i = 0; i < fmt->memplanes; i++) sizes[i] = (wh * fmt->depth[i]) / 8; - allocators[i] = fimc->alloc_ctx; - } return 0; } @@ -1300,6 +1296,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->drv_priv = fimc; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &fimc->lock; + q->dev = &fimc->pdev->dev; ret = vb2_queue_init(q); if (ret < 0) @@ -1551,11 +1548,7 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_sd; } - fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(fimc->alloc_ctx)) { - ret = PTR_ERR(fimc->alloc_ctx); - goto err_clk_dis; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc_lite_set_default_config(fimc); @@ -1563,9 +1556,6 @@ static int fimc_lite_probe(struct platform_device *pdev) fimc->index); return 0; -err_clk_dis: - if (!pm_runtime_enabled(dev)) - clk_disable(fimc->clock); err_sd: fimc_lite_unregister_capture_subdev(fimc); err_clk_put: @@ -1651,7 +1641,7 @@ static int fimc_lite_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); - vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_lite_clk_put(fimc); dev_info(dev, "Driver unloaded\n"); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 11690d563e06..9ae1e96a1bc7 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -113,7 +113,6 @@ struct flite_buffer { * @ve: exynos video device entity structure * @v4l2_dev: pointer to top the level v4l2_device * @fh: v4l2 file handle - * @alloc_ctx: videobuf2 memory allocator context * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads @@ -148,7 +147,6 @@ struct fimc_lite { struct exynos_video_entity ve; struct v4l2_device *v4l2_dev; struct v4l2_fh fh; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 55ec4c99d484..b1309e114edb 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -50,30 +50,28 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (src_vb && dst_vb) { + if (src_vb) v4l2_m2m_buf_done(src_vb, vb_state); + if (dst_vb) v4l2_m2m_buf_done(dst_vb, vb_state); + if (src_vb && dst_vb) v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, ctx->fh.m2m_ctx); - } } /* Complete the transaction which has been scheduled for execution. */ -static int fimc_m2m_shutdown(struct fimc_ctx *ctx) +static void fimc_m2m_shutdown(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; - int ret; if (!fimc_m2m_pending(fimc)) - return 0; + return; fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); - ret = wait_event_timeout(fimc->irq_queue, - !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), - FIMC_SHUTDOWN_TIMEOUT); - - return ret == 0 ? -ETIMEDOUT : ret; + wait_event_timeout(fimc->irq_queue, + !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), + FIMC_SHUTDOWN_TIMEOUT); } static int start_streaming(struct vb2_queue *q, unsigned int count) @@ -88,12 +86,10 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; - int ret; - ret = fimc_m2m_shutdown(ctx); - if (ret == -ETIMEDOUT) - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + fimc_m2m_shutdown(ctx); + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); pm_runtime_put(&ctx->fimc_dev->pdev->dev); } @@ -178,7 +174,7 @@ static void fimc_job_abort(void *priv) static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_ctx *ctx = vb2_get_drv_priv(vq); struct fimc_frame *f; @@ -195,10 +191,8 @@ static int fimc_queue_setup(struct vb2_queue *vq, return -EINVAL; *num_planes = f->fmt->memplanes; - for (i = 0; i < f->fmt->memplanes; i++) { + for (i = 0; i < f->fmt->memplanes; i++) sizes[i] = f->payload[i]; - allocators[i] = ctx->fimc_dev->alloc_ctx; - } return 0; } @@ -562,6 +556,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->fimc_dev->lock; + src_vq->dev = &ctx->fimc_dev->pdev->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -575,6 +570,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->fimc_dev->lock; + dst_vq->dev = &ctx->fimc_dev->pdev->dev; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index bf954424e7be..86e681daa89d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -649,23 +649,6 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) return 0; } -static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); - - format->colorspace = V4L2_COLORSPACE_JPEG; - format->code = s5pcsis_formats[0].code; - format->width = S5PCSIS_DEF_PIX_WIDTH; - format->height = S5PCSIS_DEF_PIX_HEIGHT; - format->field = V4L2_FIELD_NONE; - - return 0; -} - -static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = { - .open = s5pcsis_open, -}; - static struct v4l2_subdev_core_ops s5pcsis_core_ops = { .s_power = s5pcsis_s_power, .log_status = s5pcsis_log_status, diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 7383818c2be6..0fcb5c78031d 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -136,7 +136,6 @@ struct deinterlace_dev { struct dma_chan *dma_chan; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; }; struct deinterlace_ctx { @@ -799,7 +798,7 @@ struct vb2_dc_conf { static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); struct deinterlace_q_data *q_data; @@ -820,8 +819,6 @@ static int deinterlace_queue_setup(struct vb2_queue *vq, *nbuffers = count; sizes[0] = size; - alloc_ctxs[0] = ctx->dev->alloc_ctx; - dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; @@ -874,6 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = ctx->dev->v4l2_dev.dev; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -891,6 +889,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = ctx->dev->v4l2_dev.dev; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; @@ -1046,13 +1045,6 @@ static int deinterlace_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(pcdev->alloc_ctx); - goto err_ctx; - } - pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(pcdev->m2m_dev)) { v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); @@ -1064,8 +1056,6 @@ static int deinterlace_probe(struct platform_device *pdev) err_m2m: video_unregister_device(&pcdev->vfd); -err_ctx: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); unreg_dev: v4l2_device_unregister(&pcdev->v4l2_dev); rel_dma: @@ -1082,7 +1072,6 @@ static int deinterlace_remove(struct platform_device *pdev) v4l2_m2m_release(pcdev->m2m_dev); video_unregister_device(&pcdev->vfd); v4l2_device_unregister(&pcdev->v4l2_dev); - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); dma_release_channel(pcdev->dma_chan); return 0; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 9b878deb1437..af59bf4dca2d 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -973,7 +973,7 @@ static int mcam_cam_set_flip(struct mcam_camera *cam) memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CID_VFLIP; ctrl.value = flip; - return sensor_call(cam, core, s_ctrl, &ctrl); + return v4l2_s_ctrl(NULL, cam->sensor->ctrl_handler, &ctrl); } @@ -1051,7 +1051,7 @@ static int mcam_read_setup(struct mcam_camera *cam) static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, unsigned int *num_planes, unsigned int sizes[], - void *alloc_ctxs[]) + struct device *alloc_devs[]) { struct mcam_camera *cam = vb2_get_drv_priv(vq); int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2; @@ -1059,10 +1059,6 @@ static int mcam_vb_queue_setup(struct vb2_queue *vq, if (*nbufs < minbufs) *nbufs = minbufs; - if (cam->buffer_mode == B_DMA_contig) - alloc_ctxs[0] = cam->vb_alloc_ctx; - else if (cam->buffer_mode == B_DMA_sg) - alloc_ctxs[0] = cam->vb_alloc_ctx_sg; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; @@ -1271,6 +1267,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; vq->buf_struct_size = sizeof(struct mcam_vb_buffer); + vq->dev = cam->dev; INIT_LIST_HEAD(&cam->buffers); switch (cam->buffer_mode) { case B_DMA_contig: @@ -1279,9 +1276,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->mem_ops = &vb2_dma_contig_memops; cam->dma_setup = mcam_ctlr_dma_contig; cam->frame_complete = mcam_dma_contig_done; - cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev); - if (IS_ERR(cam->vb_alloc_ctx)) - return PTR_ERR(cam->vb_alloc_ctx); #endif break; case B_DMA_sg: @@ -1290,9 +1284,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->mem_ops = &vb2_dma_sg_memops; cam->dma_setup = mcam_ctlr_dma_sg; cam->frame_complete = mcam_dma_sg_done; - cam->vb_alloc_ctx_sg = vb2_dma_sg_init_ctx(cam->dev); - if (IS_ERR(cam->vb_alloc_ctx_sg)) - return PTR_ERR(cam->vb_alloc_ctx_sg); #endif break; case B_vmalloc: @@ -1309,18 +1300,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) return vb2_queue_init(vq); } -static void mcam_cleanup_vb2(struct mcam_camera *cam) -{ -#ifdef MCAM_MODE_DMA_CONTIG - if (cam->buffer_mode == B_DMA_contig) - vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx); -#endif -#ifdef MCAM_MODE_DMA_SG - if (cam->buffer_mode == B_DMA_sg) - vb2_dma_sg_cleanup_ctx(cam->vb_alloc_ctx_sg); -#endif -} - /* ---------------------------------------------------------------------- */ /* @@ -1875,7 +1854,6 @@ void mccic_shutdown(struct mcam_camera *cam) cam_warn(cam, "Removing a device with users!\n"); mcam_ctlr_power_down(cam); } - mcam_cleanup_vb2(cam); if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); video_unregister_device(&cam->vdev); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 35cd9e5aedf8..beb339f5561f 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -176,8 +176,6 @@ struct mcam_camera { /* DMA buffers - DMA modes */ struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS]; - struct vb2_alloc_ctx *vb_alloc_ctx; - struct vb2_alloc_ctx *vb_alloc_ctx_sg; /* Mode-specific ops, set at open time */ void (*dma_setup)(struct mcam_camera *cam); diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile new file mode 100644 index 000000000000..dc5cb006d600 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/Makefile @@ -0,0 +1,19 @@ + + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o mtk-vcodec-common.o + + + +mtk-vcodec-enc-y := venc/venc_vp8_if.o \ + venc/venc_h264_if.o \ + mtk_vcodec_enc.o \ + mtk_vcodec_enc_drv.o \ + mtk_vcodec_enc_pm.o \ + venc_drv_if.o \ + venc_vpu_if.o \ + + +mtk-vcodec-common-y := mtk_vcodec_intr.o \ + mtk_vcodec_util.o\ + +ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h new file mode 100644 index 000000000000..94f0a425be42 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -0,0 +1,335 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VCODEC_DRV_H_ +#define _MTK_VCODEC_DRV_H_ + +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_util.h" + +#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" +#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" +#define MTK_PLATFORM_STR "platform:mt8173" + + +#define MTK_VCODEC_MAX_PLANES 3 +#define MTK_V4L2_BENCHMARK 0 +#define WAIT_INTR_TIMEOUT_MS 1000 + +/** + * enum mtk_hw_reg_idx - MTK hw register base index + */ +enum mtk_hw_reg_idx { + VDEC_SYS, + VDEC_MISC, + VDEC_LD, + VDEC_TOP, + VDEC_CM, + VDEC_AD, + VDEC_AV, + VDEC_PP, + VDEC_HWD, + VDEC_HWQ, + VDEC_HWB, + VDEC_HWG, + NUM_MAX_VDEC_REG_BASE, + /* h264 encoder */ + VENC_SYS = NUM_MAX_VDEC_REG_BASE, + /* vp8 encoder */ + VENC_LT_SYS, + NUM_MAX_VCODEC_REG_BASE +}; + +/** + * enum mtk_instance_type - The type of an MTK Vcodec instance. + */ +enum mtk_instance_type { + MTK_INST_DECODER = 0, + MTK_INST_ENCODER = 1, +}; + +/** + * enum mtk_instance_state - The state of an MTK Vcodec instance. + * @MTK_STATE_FREE - default state when instance is created + * @MTK_STATE_INIT - vcodec instance is initialized + * @MTK_STATE_HEADER - vdec had sps/pps header parsed or venc + * had sps/pps header encoded + * @MTK_STATE_FLUSH - vdec is flushing. Only used by decoder + * @MTK_STATE_ABORT - vcodec should be aborted + */ +enum mtk_instance_state { + MTK_STATE_FREE = 0, + MTK_STATE_INIT = 1, + MTK_STATE_HEADER = 2, + MTK_STATE_FLUSH = 3, + MTK_STATE_ABORT = 4, +}; + +/** + * struct mtk_encode_param - General encoding parameters type + */ +enum mtk_encode_param { + MTK_ENCODE_PARAM_NONE = 0, + MTK_ENCODE_PARAM_BITRATE = (1 << 0), + MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), + MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), + MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), + MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), +}; + +enum mtk_fmt_type { + MTK_FMT_DEC = 0, + MTK_FMT_ENC = 1, + MTK_FMT_FRAME = 2, +}; + +/** + * struct mtk_video_fmt - Structure used to store information about pixelformats + */ +struct mtk_video_fmt { + u32 fourcc; + enum mtk_fmt_type type; + u32 num_planes; +}; + +/** + * struct mtk_codec_framesizes - Structure used to store information about + * framesizes + */ +struct mtk_codec_framesizes { + u32 fourcc; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * struct mtk_q_type - Type of queue + */ +enum mtk_q_type { + MTK_Q_DATA_SRC = 0, + MTK_Q_DATA_DST = 1, +}; + +/** + * struct mtk_q_data - Structure used to store information about queue + */ +struct mtk_q_data { + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + enum v4l2_field field; + unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; + unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; + struct mtk_video_fmt *fmt; +}; + +/** + * struct mtk_enc_params - General encoding parameters + * @bitrate: target bitrate in bits per second + * @num_b_frame: number of b frames between p-frame + * @rc_frame: frame based rate control + * @rc_mb: macroblock based rate control + * @seq_hdr_mode: H.264 sequence header is encoded separately or joined + * with the first frame + * @intra_period: I frame period + * @gop_size: group of picture size, it's used as the intra frame period + * @framerate_num: frame rate numerator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @framerate_denom: frame rate denominator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @h264_max_qp: Max value for H.264 quantization parameter + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @force_intra: force/insert intra frame + */ +struct mtk_enc_params { + unsigned int bitrate; + unsigned int num_b_frame; + unsigned int rc_frame; + unsigned int rc_mb; + unsigned int seq_hdr_mode; + unsigned int intra_period; + unsigned int gop_size; + unsigned int framerate_num; + unsigned int framerate_denom; + unsigned int h264_max_qp; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int force_intra; +}; + +/** + * struct mtk_vcodec_pm - Power management data structure + */ +struct mtk_vcodec_pm { + struct clk *vcodecpll; + struct clk *univpll_d2; + struct clk *clk_cci400_sel; + struct clk *vdecpll; + struct clk *vdec_sel; + struct clk *vencpll_d2; + struct clk *venc_sel; + struct clk *univpll1_d2; + struct clk *venc_lt_sel; + struct device *larbvdec; + struct device *larbvenc; + struct device *larbvenclt; + struct device *dev; + struct mtk_vcodec_dev *mtkdev; +}; + +/** + * struct mtk_vcodec_ctx - Context (instance) private data. + * + * @type: type of the instance - decoder or encoder + * @dev: pointer to the mtk_vcodec_dev of the device + * @list: link to ctx_list of mtk_vcodec_dev + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue + * of the context + * @id: index of the context that this structure describes + * @state: state of the context + * @param_change: indicate encode parameter type + * @enc_params: encoding parameters + * @enc_if: hoooked encoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to + * finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @encode_work: worker for the encoding + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + */ +struct mtk_vcodec_ctx { + enum mtk_instance_type type; + struct mtk_vcodec_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct mtk_q_data q_data[2]; + int id; + enum mtk_instance_state state; + enum mtk_encode_param param_change; + struct mtk_enc_params enc_params; + + struct venc_common_if *enc_if; + unsigned long drv_handle; + + int int_cond; + int int_type; + wait_queue_head_t queue; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct encode_work; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; +}; + +/** + * struct mtk_vcodec_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_enc: Video device for encoder. + * + * @m2m_dev_enc: m2m device for encoder. + * @plat_dev: platform device + * @vpu_plat_dev: mtk vpu platform device + * @ctx_list: list of struct mtk_vcodec_ctx + * @irqlock: protect data access by irq handler and work thread + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of MTK Vcodec registers. + * + * @id_counter: used to identify current opened instance + * @num_instances: counter of active MTK Vcodec instances + * + * @encode_workqueue: encode work queue + * + * @int_cond: used to identify interrupt condition happen + * @int_type: used to identify what kind of interrupt condition happen + * @dev_mutex: video_device lock + * @queue: waitqueue for waiting for completion of device commands + * + * @enc_irq: h264 encoder irq resource + * @enc_lt_irq: vp8 encoder irq resource + * + * @enc_mutex: encoder hardware lock. + * + * @pm: power management control + * @dec_capability: used to identify decode capability, ex: 4k + * @enc_capability: used to identify encode capability + */ +struct mtk_vcodec_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_enc; + + struct v4l2_m2m_dev *m2m_dev_enc; + struct platform_device *plat_dev; + struct platform_device *vpu_plat_dev; + struct list_head ctx_list; + spinlock_t irqlock; + struct mtk_vcodec_ctx *curr_ctx; + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + + unsigned long id_counter; + int num_instances; + + struct workqueue_struct *encode_workqueue; + + int int_cond; + int int_type; + struct mutex dev_mutex; + wait_queue_head_t queue; + + int enc_irq; + int enc_lt_irq; + + struct mutex enc_mutex; + + struct mtk_vcodec_pm pm; + unsigned int dec_capability; + unsigned int enc_capability; +}; + +static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mtk_vcodec_ctx, fh); +} + +static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); +} + +#endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c new file mode 100644 index 000000000000..3ed3f2d31df5 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -0,0 +1,1292 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "venc_drv_if.h" + +#define MTK_VENC_MIN_W 160U +#define MTK_VENC_MIN_H 128U +#define MTK_VENC_MAX_W 1920U +#define MTK_VENC_MAX_H 1088U +#define DFT_CFG_WIDTH MTK_VENC_MIN_W +#define DFT_CFG_HEIGHT MTK_VENC_MIN_H +#define MTK_MAX_CTRLS_HINT 20 +#define OUT_FMT_IDX 0 +#define CAP_FMT_IDX 4 + + +static void mtk_venc_worker(struct work_struct *work); + +static struct mtk_video_fmt mtk_video_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) + +static const struct mtk_codec_framesizes mtk_venc_framesizes[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, + }, +}; + +#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_venc_framesizes) + +static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + struct mtk_enc_params *p = &ctx->enc_params; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", + ctrl->val); + p->bitrate = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_BITRATE; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", + ctrl->val); + p->num_b_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", + ctrl->val); + p->rc_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", + ctrl->val); + p->h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", + ctrl->val); + p->seq_hdr_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", + ctrl->val); + p->rc_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", + ctrl->val); + p->h264_profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", + ctrl->val); + p->h264_level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", + ctrl->val); + p->intra_period = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", + ctrl->val); + p->gop_size = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); + p->force_intra = 1; + ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { + .s_ctrl = vidioc_venc_s_ctrl, +}; + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) +{ + struct mtk_video_fmt *fmt; + int i, j = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (output_queue && mtk_video_formats[i].type != MTK_FMT_FRAME) + continue; + if (!output_queue && mtk_video_formats[i].type != MTK_FMT_ENC) + continue; + + if (j == f->index) { + fmt = &mtk_video_formats[i]; + f->pixelformat = fmt->fourcc; + memset(f->reserved, 0, sizeof(f->reserved)); + return 0; + } + ++j; + } + + return -EINVAL; +} + +static int vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + int i = 0; + + if (fsize->index != 0) + return -EINVAL; + + for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) { + if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc) + continue; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = mtk_venc_framesizes[i].stepwise; + return 0; + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, false); +} + +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, true); +} + +static int vidioc_venc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); + strlcpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + + return 0; +} + +static int vidioc_venc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + ctx->enc_params.framerate_num = + a->parm.output.timeperframe.denominator; + ctx->enc_params.framerate_denom = + a->parm.output.timeperframe.numerator; + ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; + + return 0; +} + +static int vidioc_venc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.timeperframe.denominator = + ctx->enc_params.framerate_num; + a->parm.output.timeperframe.numerator = + ctx->enc_params.framerate_denom; + + return 0; +} + +static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[MTK_Q_DATA_SRC]; + + return &ctx->q_data[MTK_Q_DATA_DST]; +} + +static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &mtk_video_formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + return fmt; + } + + return NULL; +} + +/* V4L2 specification suggests the driver corrects the format struct if any of + * the dimensions is unsupported + */ +static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + int i; + + pix_fmt_mp->field = V4L2_FIELD_NONE; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pix_fmt_mp->num_planes = 1; + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + int tmp_w, tmp_h; + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W); + + /* find next closer width align 16, heign align 32, size align + * 64 rectangle + */ + tmp_w = pix_fmt_mp->width; + tmp_h = pix_fmt_mp->height; + v4l_bound_align_image(&pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W, 4, + &pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H, 5, 6); + + if (pix_fmt_mp->width < tmp_w && + (pix_fmt_mp->width + 16) <= MTK_VENC_MAX_W) + pix_fmt_mp->width += 16; + if (pix_fmt_mp->height < tmp_h && + (pix_fmt_mp->height + 32) <= MTK_VENC_MAX_H) + pix_fmt_mp->height += 32; + + mtk_v4l2_debug(0, + "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d %d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->plane_fmt[0].sizeimage, + pix_fmt_mp->plane_fmt[1].sizeimage); + + pix_fmt_mp->num_planes = fmt->num_planes; + pix_fmt_mp->plane_fmt[0].sizeimage = + pix_fmt_mp->width * pix_fmt_mp->height + + ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); + pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; + + if (pix_fmt_mp->num_planes == 2) { + pix_fmt_mp->plane_fmt[1].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + + (ALIGN(pix_fmt_mp->width, 16) * 16); + pix_fmt_mp->plane_fmt[2].sizeimage = 0; + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->width; + pix_fmt_mp->plane_fmt[2].bytesperline = 0; + } else if (pix_fmt_mp->num_planes == 3) { + pix_fmt_mp->plane_fmt[1].sizeimage = + pix_fmt_mp->plane_fmt[2].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + + ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->plane_fmt[2].bytesperline = + pix_fmt_mp->width / 2; + } + } + + for (i = 0; i < pix_fmt_mp->num_planes; i++) + memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, + sizeof(pix_fmt_mp->plane_fmt[0].reserved)); + + pix_fmt_mp->flags = 0; + memset(&pix_fmt_mp->reserved, 0x0, + sizeof(pix_fmt_mp->reserved)); + + return 0; +} + +static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, + struct venc_enc_param *param) +{ + struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC]; + struct mtk_enc_params *enc_params = &ctx->enc_params; + + switch (q_data_src->fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_I420; + break; + case V4L2_PIX_FMT_YVU420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_YV12; + break; + case V4L2_PIX_FMT_NV12M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV21M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV21; + break; + default: + mtk_v4l2_err("Unsupport fourcc =%d", q_data_src->fmt->fourcc); + break; + } + param->h264_profile = enc_params->h264_profile; + param->h264_level = enc_params->h264_level; + + /* Config visible resolution */ + param->width = q_data_src->visible_width; + param->height = q_data_src->visible_height; + /* Config coded resolution */ + param->buf_width = q_data_src->coded_width; + param->buf_height = q_data_src->coded_height; + param->frm_rate = enc_params->framerate_num / + enc_params->framerate_denom; + param->intra_period = enc_params->intra_period; + param->gop_size = enc_params->gop_size; + param->bitrate = enc_params->bitrate; + + mtk_v4l2_debug(0, + "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d", + param->input_yuv_fmt, param->h264_profile, + param->h264_level, param->width, param->height, + param->buf_width, param->buf_height, + param->frm_rate, param->bitrate, + param->gop_size, param->intra_period); +} + +static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int i, ret; + struct mtk_video_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + q_data = mtk_venc_get_q_data(ctx, f->type); + if (!q_data) { + mtk_v4l2_err("fail to get q data"); + return -EINVAL; + } + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + + q_data->fmt = fmt; + ret = vidioc_try_fmt(f, q_data->fmt); + if (ret) + return ret; + + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + q_data->field = f->fmt.pix_mp.field; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + if (ctx->state == MTK_STATE_FREE) { + ret = venc_if_init(ctx, q_data->fmt->fourcc); + if (ret) { + mtk_v4l2_err("venc_if_init failed=%d, codec type=%x", + ret, q_data->fmt->fourcc); + return -EBUSY; + } + ctx->state = MTK_STATE_INIT; + } + + return 0; +} + +static int vidioc_venc_s_fmt_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int ret, i; + struct mtk_video_fmt *fmt; + unsigned int pitch_w_div16; + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + q_data = mtk_venc_get_q_data(ctx, f->type); + if (!q_data) { + mtk_v4l2_err("fail to get q data"); + return -EINVAL; + } + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W); + + q_data->visible_width = f->fmt.pix_mp.width; + q_data->visible_height = f->fmt.pix_mp.height; + q_data->fmt = fmt; + ret = vidioc_try_fmt(f, q_data->fmt); + if (ret) + return ret; + + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + + pitch_w_div16 = DIV_ROUND_UP(q_data->visible_width, 16); + if (pitch_w_div16 % 8 != 0) { + /* Adjust returned width/height, so application could correctly + * allocate hw required memory + */ + q_data->visible_height += 32; + vidioc_try_fmt(f, q_data->fmt); + } + + q_data->field = f->fmt.pix_mp.field; + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quantization = f->fmt.pix_mp.quantization; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + return 0; +} + +static int vidioc_venc_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int i; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, f->type); + + pix->width = q_data->coded_width; + pix->height = q_data->coded_height; + pix->pixelformat = q_data->fmt->fourcc; + pix->field = q_data->field; + pix->num_planes = q_data->fmt->num_planes; + for (i = 0; i < pix->num_planes; i++) { + pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; + pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + memset(&(pix->plane_fmt[i].reserved[0]), 0x0, + sizeof(pix->plane_fmt[i].reserved)); + } + + pix->flags = 0; + pix->colorspace = ctx->colorspace; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + pix->xfer_func = ctx->xfer_func; + + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quantization; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + if (!f->fmt.pix_mp.colorspace) { + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_venc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_venc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = vidioc_venc_qbuf, + .vidioc_dqbuf = vidioc_venc_dqbuf, + + .vidioc_querycap = vidioc_venc_querycap, + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_s_parm = vidioc_venc_s_parm, + .vidioc_g_parm = vidioc_venc_g_parm, + .vidioc_s_fmt_vid_cap_mplane = vidioc_venc_s_fmt_cap, + .vidioc_s_fmt_vid_out_mplane = vidioc_venc_s_fmt_out, + + .vidioc_g_fmt_vid_cap_mplane = vidioc_venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_venc_g_fmt, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, +}; + +static int vb2ops_venc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_q_data *q_data; + unsigned int i; + + q_data = mtk_venc_get_q_data(ctx, vq->type); + + if (q_data == NULL) + return -EINVAL; + + if (*nplanes) { + for (i = 0; i < *nplanes; i++) + if (sizes[i] < q_data->sizeimage[i]) + return -EINVAL; + } else { + *nplanes = q_data->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->sizeimage[i]; + } + + return 0; +} + +static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_q_data *q_data; + int i; + + q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type); + + for (i = 0; i < q_data->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), + q_data->sizeimage[i]); + return -EINVAL; + } + } + + return 0; +} + +static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = + container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + + struct mtk_video_enc_buf *mtk_buf = + container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); + + if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + (ctx->param_change != MTK_ENCODE_PARAM_NONE)) { + mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x", + ctx->id, + mtk_buf->vb.vb2_buf.index, + ctx->param_change); + mtk_buf->param_change = ctx->param_change; + mtk_buf->enc_params = ctx->enc_params; + ctx->param_change = MTK_ENCODE_PARAM_NONE; + } + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); +} + +static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct venc_enc_param param; + int ret; + int i; + + /* Once state turn into MTK_STATE_ABORT, we need stop_streaming + * to clear it + */ + if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) { + ret = -EIO; + goto err_set_param; + } + + /* Do the initialization when both start_streaming have been called */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q)) + return 0; + } else { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q)) + return 0; + } + + mtk_venc_set_param(ctx, ¶m); + ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->param_change = MTK_ENCODE_PARAM_NONE; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->enc_params.seq_hdr_mode != + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) { + ret = venc_if_set_param(ctx, + VENC_SET_PARAM_PREPEND_HEADER, + NULL); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->state = MTK_STATE_HEADER; + } + + return 0; + +err_set_param: + for (i = 0; i < q->num_buffers; ++i) { + if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { + mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", + ctx->id, i, q->type, + (int)q->bufs[i]->state); + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]), + VB2_BUF_STATE_QUEUED); + } + } + + return ret; +} + +static void vb2ops_venc_stop_streaming(struct vb2_queue *q) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_buffer *src_buf, *dst_buf; + int ret; + + mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { + dst_buf->planes[0].bytesused = 0; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + } + } else { + while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_ERROR); + } + + if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) || + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) { + mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d", + ctx->id, q->type, + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); + return; + } + + /* Release the encoder if both streams are stopped. */ + ret = venc_if_deinit(ctx); + if (ret) + mtk_v4l2_err("venc_if_deinit failed=%d", ret); + + ctx->state = MTK_STATE_FREE; +} + +static struct vb2_ops mtk_venc_vb2_ops = { + .queue_setup = vb2ops_venc_queue_setup, + .buf_prepare = vb2ops_venc_buf_prepare, + .buf_queue = vb2ops_venc_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vb2ops_venc_start_streaming, + .stop_streaming = vb2ops_venc_stop_streaming, +}; + +static int mtk_venc_encode_header(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + struct vb2_buffer *dst_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + mtk_v4l2_debug(1, "No dst buffer"); + return -EINVAL; + } + + bs_buf.va = vb2_plane_vaddr(dst_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.size = (size_t)dst_buf->planes[0].length; + + mtk_v4l2_debug(1, + "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", + ctx->id, + dst_buf->index, bs_buf.va, + (u64)bs_buf.dma_addr, + bs_buf.size); + + ret = venc_if_encode(ctx, + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + NULL, &bs_buf, &enc_result); + + if (ret) { + dst_buf->planes[0].bytesused = 0; + ctx->state = MTK_STATE_ABORT; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + return -EINVAL; + } + + ctx->state = MTK_STATE_HEADER; + dst_buf->planes[0].bytesused = enc_result.bs_size; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE); + + return 0; +} + +static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) +{ + struct venc_enc_param enc_prm; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + struct vb2_v4l2_buffer *vb2_v4l2 = + container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + struct mtk_video_enc_buf *mtk_buf = + container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); + + int ret = 0; + + memset(&enc_prm, 0, sizeof(enc_prm)); + if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE) + return 0; + + if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) { + enc_prm.bitrate = mtk_buf->enc_params.bitrate; + mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + enc_prm.bitrate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_BITRATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) { + enc_prm.frm_rate = mtk_buf->enc_params.framerate_num / + mtk_buf->enc_params.framerate_denom; + mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + enc_prm.frm_rate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_FRAMERATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) { + enc_prm.gop_size = mtk_buf->enc_params.gop_size; + mtk_v4l2_debug(1, "change param intra period=%d", + enc_prm.gop_size); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_GOP_SIZE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) { + mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + mtk_buf->enc_params.force_intra); + if (mtk_buf->enc_params.force_intra) + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_FORCE_INTRA, + NULL); + } + + mtk_buf->param_change = MTK_ENCODE_PARAM_NONE; + + if (ret) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_err("venc_if_set_param %d failed=%d", + mtk_buf->param_change, ret); + return -1; + } + + return 0; +} + +/* + * v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker() + * to call v4l2_m2m_job_finish(). + * If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock. + * So this function must not try to acquire dev->dev_mutex. + * This means v4l2 ioctls and mtk_venc_worker() can run at the same time. + * mtk_venc_worker() should be carefully implemented to avoid bugs. + */ +static void mtk_venc_worker(struct work_struct *work) +{ + struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, + encode_work); + struct vb2_buffer *src_buf, *dst_buf; + struct venc_frm_buf frm_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + int ret, i; + struct vb2_v4l2_buffer *vb2_v4l2; + + /* check dst_buf, dst_buf may be removed in device_run + * to stored encdoe header so we need check dst_buf and + * call job_finish here to prevent recursion + */ + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + return; + } + + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + memset(&frm_buf, 0, sizeof(frm_buf)); + for (i = 0; i < src_buf->num_planes ; i++) { + frm_buf.fb_addr[i].va = vb2_plane_vaddr(src_buf, i); + frm_buf.fb_addr[i].dma_addr = + vb2_dma_contig_plane_dma_addr(src_buf, i); + frm_buf.fb_addr[i].size = + (size_t)src_buf->planes[i].length; + } + bs_buf.va = vb2_plane_vaddr(dst_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.size = (size_t)dst_buf->planes[0].length; + + mtk_v4l2_debug(2, + "Framebuf VA=%p PA=%llx Size=0x%zx;VA=%p PA=0x%llx Size=0x%zx;VA=%p PA=0x%llx Size=%zu", + frm_buf.fb_addr[0].va, + (u64)frm_buf.fb_addr[0].dma_addr, + frm_buf.fb_addr[0].size, + frm_buf.fb_addr[1].va, + (u64)frm_buf.fb_addr[1].dma_addr, + frm_buf.fb_addr[1].size, + frm_buf.fb_addr[2].va, + (u64)frm_buf.fb_addr[2].dma_addr, + frm_buf.fb_addr[2].size); + + ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, + &frm_buf, &bs_buf, &enc_result); + + vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf); + if (enc_result.is_key_frm) + vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (ret) { + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_ERROR); + dst_buf->planes[0].bytesused = 0; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + } else { + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_DONE); + dst_buf->planes[0].bytesused = enc_result.bs_size; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_DONE); + mtk_v4l2_debug(2, "venc_if_encode bs size=%d", + enc_result.bs_size); + } + + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + + mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", + src_buf->index, dst_buf->index, ret, + enc_result.bs_size); +} + +static void m2mops_venc_device_run(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->state != MTK_STATE_HEADER)) { + /* encode h264 sps/pps header */ + mtk_venc_encode_header(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); + return; + } + + mtk_venc_param_change(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); +} + +static int m2mops_venc_job_ready(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) { + mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.", + ctx->id, ctx->state); + return 0; + } + + return 1; +} + +static void m2mops_venc_job_abort(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + ctx->state = MTK_STATE_ABORT; +} + +static void m2mops_venc_lock(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + mutex_lock(&ctx->dev->dev_mutex); +} + +static void m2mops_venc_unlock(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + mutex_unlock(&ctx->dev->dev_mutex); +} + +const struct v4l2_m2m_ops mtk_venc_m2m_ops = { + .device_run = m2mops_venc_device_run, + .job_ready = m2mops_venc_job_ready, + .job_abort = m2mops_venc_job_abort, + .lock = m2mops_venc_lock, + .unlock = m2mops_venc_unlock, +}; + +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_q_data *q_data; + + ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + INIT_WORK(&ctx->encode_work, mtk_venc_worker); + + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->field = V4L2_FIELD_NONE; + + q_data->fmt = &mtk_video_formats[OUT_FMT_IDX]; + + v4l_bound_align_image(&q_data->coded_width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W, 4, + &q_data->coded_height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H, 5, 6); + + if (q_data->coded_width < DFT_CFG_WIDTH && + (q_data->coded_width + 16) <= MTK_VENC_MAX_W) + q_data->coded_width += 16; + if (q_data->coded_height < DFT_CFG_HEIGHT && + (q_data->coded_height + 32) <= MTK_VENC_MAX_H) + q_data->coded_height += 32; + + q_data->sizeimage[0] = + q_data->coded_width * q_data->coded_height+ + ((ALIGN(q_data->coded_width, 16) * 2) * 16); + q_data->bytesperline[0] = q_data->coded_width; + q_data->sizeimage[1] = + (q_data->coded_width * q_data->coded_height) / 2 + + (ALIGN(q_data->coded_width, 16) * 16); + q_data->bytesperline[1] = q_data->coded_width; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->fmt = &mtk_video_formats[CAP_FMT_IDX]; + q_data->field = V4L2_FIELD_NONE; + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = + DFT_CFG_WIDTH * DFT_CFG_HEIGHT; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0; + +} + +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) +{ + const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops; + struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; + + v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT); + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE, + 1, 4000000, 1, 4000000); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, + 0, 2, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 1); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + 0, 0, 0, 0); + v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + 0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + 0, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + if (handler->error) { + mtk_v4l2_err("Init control handler fail %d", + handler->error); + return handler->error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + + return 0; +} + +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + + /* Note: VB2_USERPTR works with dma-contig because mt8173 + * support iommu + * https://patchwork.kernel.org/patch/8335461/ + * https://patchwork.kernel.org/patch/7596181/ + */ + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf); + src_vq->ops = &mtk_venc_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = &ctx->dev->plat_dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &mtk_venc_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = &ctx->dev->plat_dev->dev; + + return vb2_queue_init(dst_vq); +} + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_unlock(&dev->enc_mutex); + return 0; +} + +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_lock(&dev->enc_mutex); + return 0; +} + +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx) +{ + venc_if_deinit(ctx); +} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h new file mode 100644 index 000000000000..d7a154a97510 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VCODEC_ENC_H_ +#define _MTK_VCODEC_ENC_H_ + +#include +#include + +#define MTK_VENC_IRQ_STATUS_SPS 0x1 +#define MTK_VENC_IRQ_STATUS_PPS 0x2 +#define MTK_VENC_IRQ_STATUS_FRM 0x4 +#define MTK_VENC_IRQ_STATUS_DRAM 0x8 +#define MTK_VENC_IRQ_STATUS_PAUSE 0x10 +#define MTK_VENC_IRQ_STATUS_SWITCH 0x20 + +#define MTK_VENC_IRQ_STATUS_OFFSET 0x05C +#define MTK_VENC_IRQ_ACK_OFFSET 0x060 + +/** + * struct mtk_video_enc_buf - Private data related to each VB2 buffer. + * @vb: Pointer to related VB2 buffer. + * @list: list that buffer link to + * @param_change: Types of encode parameter change before encoding this + * buffer + * @enc_params: Encode parameters changed before encode this buffer + */ +struct mtk_video_enc_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + u32 param_change; + struct mtk_enc_params enc_params; +}; + +extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops; +extern const struct v4l2_m2m_ops mtk_venc_m2m_ops; + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx); +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx); + +#endif /* _MTK_VCODEC_ENC_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c new file mode 100644 index 000000000000..e277b7c23516 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -0,0 +1,439 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + +module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR); +module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR); + +/* Wake up context wait_queue */ +static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason) +{ + ctx->int_cond = 1; + ctx->int_type = reason; + wake_up_interruptible(&ctx->queue); +} + +static void clean_irq_status(unsigned int irq_status, void __iomem *addr) +{ + if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) + writel(MTK_VENC_IRQ_STATUS_PAUSE, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH) + writel(MTK_VENC_IRQ_STATUS_SWITCH, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_DRAM) + writel(MTK_VENC_IRQ_STATUS_DRAM, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SPS) + writel(MTK_VENC_IRQ_STATUS_SPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_PPS) + writel(MTK_VENC_IRQ_STATUS_PPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_FRM) + writel(MTK_VENC_IRQ_STATUS_FRM, addr); + +} +static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + unsigned long flags; + void __iomem *addr; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + + mtk_v4l2_debug(1, "id=%d", ctx->id); + addr = dev->reg_base[VENC_SYS] + MTK_VENC_IRQ_ACK_OFFSET; + + ctx->irq_status = readl(dev->reg_base[VENC_SYS] + + (MTK_VENC_IRQ_STATUS_OFFSET)); + + clean_irq_status(ctx->irq_status, addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); + return IRQ_HANDLED; +} + +static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + unsigned long flags; + void __iomem *addr; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + + mtk_v4l2_debug(1, "id=%d", ctx->id); + ctx->irq_status = readl(dev->reg_base[VENC_LT_SYS] + + (MTK_VENC_IRQ_STATUS_OFFSET)); + + addr = dev->reg_base[VENC_LT_SYS] + MTK_VENC_IRQ_ACK_OFFSET; + + clean_irq_status(ctx->irq_status, addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); + return IRQ_HANDLED; +} + +static void mtk_vcodec_enc_reset_handler(void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + + mtk_v4l2_debug(0, "Watchdog timeout!!"); + + mutex_lock(&dev->dev_mutex); + list_for_each_entry(ctx, &dev->ctx_list, list) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", + ctx->id); + } + mutex_unlock(&dev->dev_mutex); +} + +static int fops_vcodec_open(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = NULL; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_lock(&dev->dev_mutex); + /* + * Use simple counter to uniquely identify this context. Only + * used for logging. + */ + ctx->id = dev->id_counter++; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + INIT_LIST_HEAD(&ctx->list); + ctx->dev = dev; + init_waitqueue_head(&ctx->queue); + + ctx->type = MTK_INST_ENCODER; + ret = mtk_vcodec_enc_ctrls_setup(ctx); + if (ret) { + mtk_v4l2_err("Failed to setup controls() (%d)", + ret); + goto err_ctrls_setup; + } + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, + &mtk_vcodec_enc_queue_init); + if (IS_ERR((__force void *)ctx->m2m_ctx)) { + ret = PTR_ERR((__force void *)ctx->m2m_ctx); + mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", + ret); + goto err_m2m_ctx_init; + } + mtk_vcodec_enc_set_default_params(ctx); + + if (v4l2_fh_is_singular(&ctx->fh)) { + /* + * vpu_load_firmware checks if it was loaded already and + * does nothing in that case + */ + ret = vpu_load_firmware(dev->vpu_plat_dev); + if (ret < 0) { + /* + * Return 0 if downloading firmware successfully, + * otherwise it is failed + */ + mtk_v4l2_err("vpu_load_firmware failed!"); + goto err_load_fw; + } + + dev->enc_capability = + vpu_get_venc_hw_capa(dev->vpu_plat_dev); + mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); + } + + mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", + ctx->id, ctx, ctx->m2m_ctx); + + dev->num_instances++; + list_add(&ctx->list, &dev->ctx_list); + + mutex_unlock(&dev->dev_mutex); + mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), + ctx->id); + return ret; + + /* Deinit when failure occurred */ +err_load_fw: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_ctrls_setup: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + + mtk_v4l2_debug(1, "[%d] encoder", ctx->id); + mutex_lock(&dev->dev_mutex); + + mtk_vcodec_enc_release(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + + list_del_init(&ctx->list); + dev->num_instances--; + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + return 0; +} + +static const struct v4l2_file_operations mtk_vcodec_fops = { + .owner = THIS_MODULE, + .open = fops_vcodec_open, + .release = fops_vcodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int mtk_vcodec_probe(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev; + struct video_device *vfd_enc; + struct resource *res; + int i, j, ret; + DEFINE_DMA_ATTRS(attrs); + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->ctx_list); + dev->plat_dev = pdev; + + dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev); + if (dev->vpu_plat_dev == NULL) { + mtk_v4l2_err("[VPU] vpu device in not ready"); + return -EPROBE_DEFER; + } + + vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler, + dev, VPU_RST_ENC); + + ret = mtk_vcodec_init_enc_pm(dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get mt vcodec clock source!"); + return ret; + } + + for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, j); + if (res == NULL) { + dev_err(&pdev->dev, "get memory resource failed."); + ret = -ENXIO; + goto err_res; + } + dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((__force void *)dev->reg_base[i])) { + ret = PTR_ERR((__force void *)dev->reg_base[i]); + goto err_res; + } + mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get irq resource"); + ret = -ENOENT; + goto err_res; + } + + dev->enc_irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, dev->enc_irq, + mtk_vcodec_enc_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "Failed to install dev->enc_irq %d (%d)", + dev->enc_irq, + ret); + ret = -EINVAL; + goto err_res; + } + + dev->enc_lt_irq = platform_get_irq(pdev, 1); + ret = devm_request_irq(&pdev->dev, + dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to install dev->enc_lt_irq %d (%d)", + dev->enc_lt_irq, ret); + ret = -EINVAL; + goto err_res; + } + + disable_irq(dev->enc_irq); + disable_irq(dev->enc_lt_irq); /* VENC_LT */ + mutex_init(&dev->enc_mutex); + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irqlock); + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + "[MTK_V4L2_VENC]"); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + mtk_v4l2_err("v4l2_device_register err=%d", ret); + goto err_res; + } + + init_waitqueue_head(&dev->queue); + + /* allocate video device for encoder and register it */ + vfd_enc = video_device_alloc(); + if (!vfd_enc) { + mtk_v4l2_err("Failed to allocate video device"); + ret = -ENOMEM; + goto err_enc_alloc; + } + vfd_enc->fops = &mtk_vcodec_fops; + vfd_enc->ioctl_ops = &mtk_venc_ioctl_ops; + vfd_enc->release = video_device_release; + vfd_enc->lock = &dev->dev_mutex; + vfd_enc->v4l2_dev = &dev->v4l2_dev; + vfd_enc->vfl_dir = VFL_DIR_M2M; + vfd_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + + snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s", + MTK_VCODEC_ENC_NAME); + video_set_drvdata(vfd_enc, dev); + dev->vfd_enc = vfd_enc; + platform_set_drvdata(pdev, dev); + + dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); + if (IS_ERR((__force void *)dev->m2m_dev_enc)) { + mtk_v4l2_err("Failed to init mem2mem enc device"); + ret = PTR_ERR((__force void *)dev->m2m_dev_enc); + goto err_enc_mem_init; + } + + dev->encode_workqueue = + alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME, + WQ_MEM_RECLAIM | + WQ_FREEZABLE); + if (!dev->encode_workqueue) { + mtk_v4l2_err("Failed to create encode workqueue"); + ret = -EINVAL; + goto err_event_workq; + } + + ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1); + if (ret) { + mtk_v4l2_err("Failed to register video device"); + goto err_enc_reg; + } + + /* Avoid the iommu eat big hunks */ + dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs); + + mtk_v4l2_debug(0, "encoder registered as /dev/video%d", + vfd_enc->num); + + return 0; + +err_enc_reg: + destroy_workqueue(dev->encode_workqueue); +err_event_workq: + v4l2_m2m_release(dev->m2m_dev_enc); +err_enc_mem_init: + video_unregister_device(vfd_enc); +err_enc_alloc: + v4l2_device_unregister(&dev->v4l2_dev); +err_res: + mtk_vcodec_release_enc_pm(dev); + return ret; +} + +static const struct of_device_id mtk_vcodec_enc_match[] = { + {.compatible = "mediatek,mt8173-vcodec-enc",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); + +static int mtk_vcodec_enc_remove(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + + mtk_v4l2_debug_enter(); + flush_workqueue(dev->encode_workqueue); + destroy_workqueue(dev->encode_workqueue); + if (dev->m2m_dev_enc) + v4l2_m2m_release(dev->m2m_dev_enc); + + if (dev->vfd_enc) + video_unregister_device(dev->vfd_enc); + + v4l2_device_unregister(&dev->v4l2_dev); + mtk_vcodec_release_enc_pm(dev); + return 0; +} + +static struct platform_driver mtk_vcodec_enc_driver = { + .probe = mtk_vcodec_probe, + .remove = mtk_vcodec_enc_remove, + .driver = { + .name = MTK_VCODEC_ENC_NAME, + .of_match_table = mtk_vcodec_enc_match, + }, +}; + +module_platform_driver(mtk_vcodec_enc_driver); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c new file mode 100644 index 000000000000..3e73e9db781f --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -0,0 +1,137 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include + +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + + +int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) +{ + struct device_node *node; + struct platform_device *pdev; + struct device *dev; + struct mtk_vcodec_pm *pm; + int ret = 0; + + pdev = mtkdev->plat_dev; + pm = &mtkdev->pm; + memset(pm, 0, sizeof(struct mtk_vcodec_pm)); + pm->mtkdev = mtkdev; + pm->dev = &pdev->dev; + dev = &pdev->dev; + + node = of_parse_phandle(dev->of_node, "mediatek,larb", 0); + if (!node) { + mtk_v4l2_err("no mediatek,larb found"); + return -1; + } + pdev = of_find_device_by_node(node); + if (!pdev) { + mtk_v4l2_err("no mediatek,larb device found"); + return -1; + } + pm->larbvenc = &pdev->dev; + + node = of_parse_phandle(dev->of_node, "mediatek,larb", 1); + if (!node) { + mtk_v4l2_err("no mediatek,larb found"); + return -1; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + mtk_v4l2_err("no mediatek,larb device found"); + return -1; + } + + pm->larbvenclt = &pdev->dev; + pdev = mtkdev->plat_dev; + pm->dev = &pdev->dev; + + pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src"); + if (IS_ERR(pm->vencpll_d2)) { + mtk_v4l2_err("devm_clk_get vencpll_d2 fail"); + ret = PTR_ERR(pm->vencpll_d2); + } + + pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel"); + if (IS_ERR(pm->venc_sel)) { + mtk_v4l2_err("devm_clk_get venc_sel fail"); + ret = PTR_ERR(pm->venc_sel); + } + + pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src"); + if (IS_ERR(pm->univpll1_d2)) { + mtk_v4l2_err("devm_clk_get univpll1_d2 fail"); + ret = PTR_ERR(pm->univpll1_d2); + } + + pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); + if (IS_ERR(pm->venc_lt_sel)) { + mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); + ret = PTR_ERR(pm->venc_lt_sel); + } + + return ret; +} + +void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) +{ +} + + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) +{ + int ret; + + ret = clk_prepare_enable(pm->venc_sel); + if (ret) + mtk_v4l2_err("clk_prepare_enable fail %d", ret); + + ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2); + if (ret) + mtk_v4l2_err("clk_set_parent fail %d", ret); + + ret = clk_prepare_enable(pm->venc_lt_sel); + if (ret) + mtk_v4l2_err("clk_prepare_enable fail %d", ret); + + ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2); + if (ret) + mtk_v4l2_err("clk_set_parent fail %d", ret); + + ret = mtk_smi_larb_get(pm->larbvenc); + if (ret) + mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret); + + ret = mtk_smi_larb_get(pm->larbvenclt); + if (ret) + mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret); + +} + +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) +{ + mtk_smi_larb_put(pm->larbvenc); + mtk_smi_larb_put(pm->larbvenclt); + clk_disable_unprepare(pm->venc_lt_sel); + clk_disable_unprepare(pm->venc_sel); +} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h new file mode 100644 index 000000000000..f32167138976 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VCODEC_ENC_PM_H_ +#define _MTK_VCODEC_ENC_PM_H_ + +#include "mtk_vcodec_drv.h" + +int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *dev); +void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *dev); + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); + +#endif /* _MTK_VCODEC_ENC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c new file mode 100644 index 000000000000..52e7e5c9afa0 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command, + unsigned int timeout_ms) +{ + wait_queue_head_t *waitqueue; + long timeout_jiff, ret; + int status = 0; + + waitqueue = (wait_queue_head_t *)&ctx->queue; + timeout_jiff = msecs_to_jiffies(timeout_ms); + + ret = wait_event_interruptible_timeout(*waitqueue, + (ctx->int_cond && + (ctx->int_type == command)), + timeout_jiff); + + if (!ret) { + status = -1; /* timeout */ + mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!", + ctx->id, ctx->type, command, timeout_ms, + ctx->int_cond, ctx->int_type); + } else if (-ERESTARTSYS == ret) { + mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", + ctx->id, ctx->type, command, ctx->int_cond, + ctx->int_type); + status = -1; + } + + ctx->int_cond = 0; + ctx->int_type = 0; + + return status; +} +EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h new file mode 100644 index 000000000000..33e890f5aa9c --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VCODEC_INTR_H_ +#define _MTK_VCODEC_INTR_H_ + +#define MTK_INST_IRQ_RECEIVED 0x1 +#define MTK_INST_WORK_THREAD_ABORT_DONE 0x2 + +struct mtk_vcodec_ctx; + +/* timeout is ms */ +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *data, int command, + unsigned int timeout_ms); + +#endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c new file mode 100644 index 000000000000..5e3651372a3c --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + +/* For encoder, this will enable logs in venc/*/ +bool mtk_vcodec_dbg; +EXPORT_SYMBOL(mtk_vcodec_dbg); + +/* The log level of v4l2 encoder or decoder driver. + * That is, files under mtk-vcodec/. + */ +int mtk_v4l2_dbg_level; +EXPORT_SYMBOL(mtk_v4l2_dbg_level); + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx) +{ + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + + if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { + mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); + return NULL; + } + return ctx->dev->reg_base[reg_idx]; +} +EXPORT_SYMBOL(mtk_vcodec_get_reg_addr); + +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); + + if (!mem->va) { + mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), + size); + return -ENOMEM; + } + + memset(mem->va, 0, size); + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + return 0; +} +EXPORT_SYMBOL(mtk_vcodec_mem_alloc); + +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + if (!mem->va) { + mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), + size); + return; + } + + dma_free_coherent(dev, size, mem->va, mem->dma_addr); + mem->va = NULL; + mem->dma_addr = 0; + mem->size = 0; + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); +} +EXPORT_SYMBOL(mtk_vcodec_mem_free); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h new file mode 100644 index 000000000000..d6345fc04840 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VCODEC_UTIL_H_ +#define _MTK_VCODEC_UTIL_H_ + +#include +#include + +struct mtk_vcodec_mem { + size_t size; + void *va; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_ctx; + +extern int mtk_v4l2_dbg_level; +extern bool mtk_vcodec_dbg; + +#define DEBUG 1 + +#if defined(DEBUG) + +#define mtk_v4l2_debug(level, fmt, args...) \ + do { \ + if (mtk_v4l2_dbg_level >= level) \ + pr_info("[MTK_V4L2] level=%d %s(),%d: " fmt "\n",\ + level, __func__, __LINE__, ##args); \ + } while (0) + +#define mtk_v4l2_err(fmt, args...) \ + pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \ + ##args) + + +#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") +#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") + +#define mtk_vcodec_debug(h, fmt, args...) \ + do { \ + if (mtk_vcodec_dbg) \ + pr_info("[MTK_VCODEC][%d]: %s() " fmt "\n", \ + ((struct mtk_vcodec_ctx *)h->ctx)->id, \ + __func__, ##args); \ + } while (0) + +#define mtk_vcodec_err(h, fmt, args...) \ + pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \ + ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args) + +#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") +#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") + +#else + +#define mtk_v4l2_debug(level, fmt, args...) +#define mtk_v4l2_err(fmt, args...) +#define mtk_v4l2_debug_enter() +#define mtk_v4l2_debug_leave() + +#define mtk_vcodec_debug(h, fmt, args...) +#define mtk_vcodec_err(h, fmt, args...) +#define mtk_vcodec_debug_enter(h) +#define mtk_vcodec_debug_leave(h) + +#endif + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx); +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c new file mode 100644 index 000000000000..9a600525b3c1 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao + * Daniel Hsiao + * PoChun Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" +#include "mtk_vpu.h" + +static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; + +#define H264_FILLER_MARKER_SIZE ARRAY_SIZE(h264_filler_marker) +#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098 + +/** + * enum venc_h264_vpu_work_buf - h264 encoder buffer index + */ +enum venc_h264_vpu_work_buf { + VENC_H264_VPU_WORK_BUF_RC_INFO, + VENC_H264_VPU_WORK_BUF_RC_CODE, + VENC_H264_VPU_WORK_BUF_REC_LUMA, + VENC_H264_VPU_WORK_BUF_REC_CHROMA, + VENC_H264_VPU_WORK_BUF_REF_LUMA, + VENC_H264_VPU_WORK_BUF_REF_CHROMA, + VENC_H264_VPU_WORK_BUF_MV_INFO_1, + VENC_H264_VPU_WORK_BUF_MV_INFO_2, + VENC_H264_VPU_WORK_BUF_SKIP_FRAME, + VENC_H264_VPU_WORK_BUF_MAX, +}; + +/** + * enum venc_h264_bs_mode - for bs_mode argument in h264_enc_vpu_encode + */ +enum venc_h264_bs_mode { + H264_BS_MODE_SPS, + H264_BS_MODE_PPS, + H264_BS_MODE_FRAME, +}; + +/* + * struct venc_h264_vpu_config - Structure for h264 encoder configuration + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to + * hardware requirements. + * @buf_h: buffer height + * @gop_size: group of picture size (idr frame) + * @intra_period: intra frame period + * @framerate: frame rate in fps + * @profile: as specified in standard + * @level: as specified in standard + * @wfd: WFD mode 1:on, 0:off + */ +struct venc_h264_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 intra_period; + u32 framerate; + u32 profile; + u32 level; + u32 wfd; +}; + +/* + * struct venc_h264_vpu_buf - Structure for buffer information + * @align: buffer alignment (in bytes) + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_h264_vpu_buf { + u32 align; + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_h264_vsi - Structure for VPU driver control and info share + * This structure is allocated in VPU side and shared to AP side. + * @config: h264 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_h264_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_h264_vsi { + struct venc_h264_vpu_config config; + struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_h264_inst - h264 encoder AP driver instance + * @hw_base: h264 encoder hardware register base + * @work_bufs: working buffer + * @pps_buf: buffer to store the pps bitstream + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count + * @prepend_hdr: when the v4l2 layer send VENC_SET_PARAM_PREPEND_HEADER cmd + * through h264_enc_set_param interface, it will set this flag and prepend the + * sps/pps in h264_enc_encode function. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_h264_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; + struct mtk_vcodec_mem pps_buf; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int prepend_hdr; + struct venc_vpu_inst vpu_inst; + struct venc_h264_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline void h264_write_reg(struct venc_h264_inst *inst, u32 addr, + u32 val) +{ + writel(val, inst->hw_base + addr); +} + +static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +static unsigned int h264_get_profile(struct venc_h264_inst *inst, + unsigned int profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + mtk_vcodec_err(inst, "unsupported CONSTRAINED_BASELINE"); + return 0; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + mtk_vcodec_err(inst, "unsupported EXTENDED"); + return 0; + default: + mtk_vcodec_debug(inst, "unsupported profile %d", profile); + return 100; + } +} + +static unsigned int h264_get_level(struct venc_h264_inst *inst, + unsigned int level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + mtk_vcodec_err(inst, "unsupported 1B"); + return 0; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + default: + mtk_vcodec_debug(inst, "unsupported level %d", level); + return 31; + } +} + +static void h264_enc_free_work_buf(struct venc_h264_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Except the SKIP_FRAME buffers, + * other buffers need to be freed by AP. + */ + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { + if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME) + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); + + mtk_vcodec_debug_leave(inst); +} + +static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) +{ + int i; + int ret = 0; + struct venc_h264_vpu_buf *wb = inst->vsi->work_bufs; + + mtk_vcodec_debug_enter(inst); + + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { + /* + * This 'wb' structure is set by VPU side and shared to AP for + * buffer allocation and IO virtual addr mapping. For most of + * the buffers, AP will allocate the buffer according to 'size' + * field and store the IO virtual addr in 'iova' field. There + * are two exceptions: + * (1) RC_CODE buffer, it's pre-allocated in the VPU side, and + * save the VPU addr in the 'vpua' field. The AP will translate + * the VPU addr to the corresponding IO virtual addr and store + * in 'iova' field for reg setting in VPU side. + * (2) SKIP_FRAME buffer, it's pre-allocated in the VPU side, + * and save the VPU addr in the 'vpua' field. The AP will + * translate the VPU addr to the corresponding AP side virtual + * address and do some memcpy access to move to bitstream buffer + * assigned by v4l2 layer. + */ + inst->work_bufs[i].size = wb[i].size; + if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) { + inst->work_bufs[i].va = vpu_mapping_dm_addr( + inst->vpu_inst.dev, wb[i].vpua); + inst->work_bufs[i].dma_addr = 0; + } else { + ret = mtk_vcodec_mem_alloc(inst->ctx, + &inst->work_bufs[i]); + if (ret) { + mtk_vcodec_err(inst, + "cannot allocate buf %d", i); + goto err_alloc; + } + /* + * This RC_CODE is pre-allocated by VPU and saved in VPU + * addr. So we need use memcpy to copy RC_CODE from VPU + * addr into IO virtual addr in 'iova' field for reg + * setting in VPU side. + */ + if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) { + void *tmp_va; + + tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, + wb[i].vpua); + memcpy(inst->work_bufs[i].va, tmp_va, + wb[i].size); + } + } + wb[i].iova = inst->work_bufs[i].dma_addr; + + mtk_vcodec_debug(inst, + "work_buf[%d] va=0x%p iova=%pad size=%zu", + i, inst->work_bufs[i].va, + &inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); + } + + /* the pps_buf is used by AP side only */ + inst->pps_buf.size = 128; + ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf); + if (ret) { + mtk_vcodec_err(inst, "cannot allocate pps_buf"); + goto err_alloc; + } + + mtk_vcodec_debug_leave(inst); + + return ret; + +err_alloc: + h264_enc_free_work_buf(inst); + + return ret; +} + +static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) +{ + unsigned int irq_status = 0; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + + if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); + } + return irq_status; +} + +static int h264_encode_sps(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL, + bs_buf, bs_size); + if (ret) + return ret; + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_SPS) { + mtk_vcodec_err(inst, "expect irq status %d", + MTK_VENC_IRQ_STATUS_SPS); + return -EINVAL; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + + return ret; +} + +static int h264_encode_pps(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL, + bs_buf, bs_size); + if (ret) + return ret; + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_PPS) { + mtk_vcodec_err(inst, "expect irq status %d", + MTK_VENC_IRQ_STATUS_PPS); + return -EINVAL; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + + return ret; +} + +static int h264_encode_header(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int bs_size_sps; + unsigned int bs_size_pps; + + ret = h264_encode_sps(inst, bs_buf, &bs_size_sps); + if (ret) + return ret; + + ret = h264_encode_pps(inst, &inst->pps_buf, &bs_size_pps); + if (ret) + return ret; + + memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps); + *bs_size = bs_size_sps + bs_size_pps; + + return ret; +} + +static int h264_encode_frame(struct venc_h264_inst *inst, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, + bs_buf, bs_size); + if (ret) + return ret; + + /* + * skip frame case: The skip frame buffer is composed by vpu side only, + * it does not trigger the hw, so skip the wait interrupt operation. + */ + if (inst->vpu_inst.state == VEN_IPI_MSG_ENC_STATE_SKIP) { + *bs_size = inst->vpu_inst.bs_size; + memcpy(bs_buf->va, + inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va, + *bs_size); + ++inst->frm_cnt; + return ret; + } + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { + mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + return -EIO; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + + ++inst->frm_cnt; + mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-", + inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm); + + return ret; +} + +static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, + int size) +{ + unsigned char *p = buf; + + if (size < H264_FILLER_MARKER_SIZE) { + mtk_vcodec_err(inst, "filler size too small %d", size); + return; + } + + memcpy(p, h264_filler_marker, ARRAY_SIZE(h264_filler_marker)); + size -= H264_FILLER_MARKER_SIZE; + p += H264_FILLER_MARKER_SIZE; + memset(p, 0xff, size); +} + +static int h264_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +{ + int ret = 0; + struct venc_h264_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; + inst->vpu_inst.id = IPI_VENC_H264; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + (*handle) = (unsigned long)inst; + + return ret; +} + +static int h264_enc_encode(unsigned long handle, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_debug(inst, "opt %d ->", opt); + + enable_irq(ctx->dev->enc_irq); + + switch (opt) { + case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: { + unsigned int bs_size_hdr; + + ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); + if (ret) + goto encode_err; + + result->bs_size = bs_size_hdr; + result->is_key_frm = false; + break; + } + + case VENC_START_OPT_ENCODE_FRAME: { + int hdr_sz; + int hdr_sz_ext; + int filler_sz = 0; + const int bs_alignment = 128; + struct mtk_vcodec_mem tmp_bs_buf; + unsigned int bs_size_hdr; + unsigned int bs_size_frm; + + if (!inst->prepend_hdr) { + ret = h264_encode_frame(inst, frm_buf, bs_buf, + &result->bs_size); + if (ret) + goto encode_err; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + } + + mtk_vcodec_debug(inst, "h264_encode_frame prepend SPS/PPS"); + + ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); + if (ret) + goto encode_err; + + hdr_sz = bs_size_hdr; + hdr_sz_ext = (hdr_sz & (bs_alignment - 1)); + if (hdr_sz_ext) { + filler_sz = bs_alignment - hdr_sz_ext; + if (hdr_sz_ext + H264_FILLER_MARKER_SIZE > bs_alignment) + filler_sz += bs_alignment; + h264_encode_filler(inst, bs_buf->va + hdr_sz, + filler_sz); + } + + tmp_bs_buf.va = bs_buf->va + hdr_sz + filler_sz; + tmp_bs_buf.dma_addr = bs_buf->dma_addr + hdr_sz + filler_sz; + tmp_bs_buf.size = bs_buf->size - (hdr_sz + filler_sz); + + ret = h264_encode_frame(inst, frm_buf, &tmp_bs_buf, + &bs_size_frm); + if (ret) + goto encode_err; + + result->bs_size = hdr_sz + filler_sz + bs_size_frm; + + mtk_vcodec_debug(inst, "hdr %d filler %d frame %d bs %d", + hdr_sz, filler_sz, bs_size_frm, + result->bs_size); + + inst->prepend_hdr = 0; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + } + + default: + mtk_vcodec_err(inst, "venc_start_opt %d not supported", opt); + ret = -EINVAL; + break; + } + +encode_err: + + disable_irq(ctx->dev->enc_irq); + mtk_vcodec_debug(inst, "opt %d <-", opt); + + return ret; +} + +static int h264_enc_set_param(unsigned long handle, + enum venc_set_param_type type, + struct venc_enc_param *enc_prm) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + + mtk_vcodec_debug(inst, "->type=%d", type); + + switch (type) { + case VENC_SET_PARAM_ENC: + inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi->config.bitrate = enc_prm->bitrate; + inst->vsi->config.pic_w = enc_prm->width; + inst->vsi->config.pic_h = enc_prm->height; + inst->vsi->config.buf_w = enc_prm->buf_width; + inst->vsi->config.buf_h = enc_prm->buf_height; + inst->vsi->config.gop_size = enc_prm->gop_size; + inst->vsi->config.framerate = enc_prm->frm_rate; + inst->vsi->config.intra_period = enc_prm->intra_period; + inst->vsi->config.profile = + h264_get_profile(inst, enc_prm->h264_profile); + inst->vsi->config.level = + h264_get_level(inst, enc_prm->h264_level); + inst->vsi->config.wfd = 0; + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + if (ret) + break; + if (inst->work_buf_allocated) { + h264_enc_free_work_buf(inst); + inst->work_buf_allocated = false; + } + ret = h264_enc_alloc_work_buf(inst); + if (ret) + break; + inst->work_buf_allocated = true; + break; + + case VENC_SET_PARAM_PREPEND_HEADER: + inst->prepend_hdr = 1; + mtk_vcodec_debug(inst, "set prepend header mode"); + break; + + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int h264_enc_deinit(unsigned long handle) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + h264_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +static struct venc_common_if venc_h264_if = { + h264_enc_init, + h264_enc_encode, + h264_enc_set_param, + h264_enc_deinit, +}; + +struct venc_common_if *get_h264_enc_comm_if(void); + +struct venc_common_if *get_h264_enc_comm_if(void) +{ + return &venc_h264_if; +} diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c new file mode 100644 index 000000000000..60bbcd2a0510 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * PoChun Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" +#include "mtk_vpu.h" + +#define VENC_BITSTREAM_FRAME_SIZE 0x0098 +#define VENC_BITSTREAM_HEADER_LEN 0x00e8 + +/* This ac_tag is vp8 frame tag. */ +#define MAX_AC_TAG_SIZE 10 + +/** + * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index + */ +enum venc_vp8_vpu_work_buf { + VENC_VP8_VPU_WORK_BUF_LUMA, + VENC_VP8_VPU_WORK_BUF_LUMA2, + VENC_VP8_VPU_WORK_BUF_LUMA3, + VENC_VP8_VPU_WORK_BUF_CHROMA, + VENC_VP8_VPU_WORK_BUF_CHROMA2, + VENC_VP8_VPU_WORK_BUF_CHROMA3, + VENC_VP8_VPU_WORK_BUF_MV_INFO, + VENC_VP8_VPU_WORK_BUF_BS_HEADER, + VENC_VP8_VPU_WORK_BUF_PROB_BUF, + VENC_VP8_VPU_WORK_BUF_RC_INFO, + VENC_VP8_VPU_WORK_BUF_RC_CODE, + VENC_VP8_VPU_WORK_BUF_RC_CODE2, + VENC_VP8_VPU_WORK_BUF_RC_CODE3, + VENC_VP8_VPU_WORK_BUF_MAX, +}; + +/* + * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width (with 16 alignment). Buffer size is stream resolution + * in pixels aligned to hardware requirements. + * @buf_h: buffer height (with 16 alignment) + * @gop_size: group of picture size (key frame) + * @framerate: frame rate in fps + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + */ +struct venc_vp8_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 framerate; + u32 ts_mode; +}; + +/* + * struct venc_vp8_vpu_buf -Structure for buffer information + * @align: buffer alignment (in bytes) + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_vp8_vpu_buf { + u32 align; + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_vp8_vsi - Structure for VPU driver control and info share + * This structure is allocated in VPU side and shared to AP side. + * @config: vp8 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_vp8_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_vp8_vsi { + struct venc_vp8_vpu_config config; + struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_vp8_inst - vp8 encoder AP driver instance + * @hw_base: vp8 encoder hardware register base + * @work_bufs: working buffer + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count, it's used for I-frame judgement and + * reset when force intra cmd received. + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_vp8_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int ts_mode; + struct venc_vpu_inst vpu_inst; + struct venc_vp8_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline void vp8_enc_write_reg(struct venc_vp8_inst *inst, u32 addr, + u32 val) +{ + writel(val, inst->hw_base + addr); +} + +static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Buffers need to be freed by AP. */ + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if ((inst->work_bufs[i].size == 0)) + continue; + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_debug_leave(inst); +} + +static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) +{ + int i; + int ret = 0; + struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs; + + mtk_vcodec_debug_enter(inst); + + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if ((wb[i].size == 0)) + continue; + /* + * This 'wb' structure is set by VPU side and shared to AP for + * buffer allocation and IO virtual addr mapping. For most of + * the buffers, AP will allocate the buffer according to 'size' + * field and store the IO virtual addr in 'iova' field. For the + * RC_CODEx buffers, they are pre-allocated in the VPU side + * because they are inside VPU SRAM, and save the VPU addr in + * the 'vpua' field. The AP will translate the VPU addr to the + * corresponding IO virtual addr and store in 'iova' field. + */ + inst->work_bufs[i].size = wb[i].size; + ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); + if (ret) { + mtk_vcodec_err(inst, + "cannot alloc work_bufs[%d]", i); + goto err_alloc; + } + /* + * This RC_CODEx is pre-allocated by VPU and saved in VPU addr. + * So we need use memcpy to copy RC_CODEx from VPU addr into IO + * virtual addr in 'iova' field for reg setting in VPU side. + */ + if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) { + void *tmp_va; + + tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, + wb[i].vpua); + memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size); + } + wb[i].iova = inst->work_bufs[i].dma_addr; + + mtk_vcodec_debug(inst, + "work_bufs[%d] va=0x%p,iova=%pad,size=%zu", + i, inst->work_bufs[i].va, + &inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); + } + + mtk_vcodec_debug_leave(inst); + + return ret; + +err_alloc: + vp8_enc_free_work_buf(inst); + + return ret; +} + +static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst) +{ + unsigned int irq_status = 0; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + + if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "isr return %x", irq_status); + } + return irq_status; +} + +/* + * Compose ac_tag, bitstream header and bitstream payload into + * one bitstream buffer. + */ +static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + unsigned int not_key; + u32 bs_frm_size; + u32 bs_hdr_len; + unsigned int ac_tag_size; + u8 ac_tag[MAX_AC_TAG_SIZE]; + u32 tag; + + bs_frm_size = vp8_enc_read_reg(inst, VENC_BITSTREAM_FRAME_SIZE); + bs_hdr_len = vp8_enc_read_reg(inst, VENC_BITSTREAM_HEADER_LEN); + + /* if a frame is key frame, not_key is 0 */ + not_key = !inst->vpu_inst.is_key_frm; + tag = (bs_hdr_len << 5) | 0x10 | not_key; + ac_tag[0] = tag & 0xff; + ac_tag[1] = (tag >> 8) & 0xff; + ac_tag[2] = (tag >> 16) & 0xff; + + /* key frame */ + if (not_key == 0) { + ac_tag_size = MAX_AC_TAG_SIZE; + ac_tag[3] = 0x9d; + ac_tag[4] = 0x01; + ac_tag[5] = 0x2a; + ac_tag[6] = inst->vsi->config.pic_w; + ac_tag[7] = inst->vsi->config.pic_w >> 8; + ac_tag[8] = inst->vsi->config.pic_h; + ac_tag[9] = inst->vsi->config.pic_h >> 8; + } else { + ac_tag_size = 3; + } + + if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) { + mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)", + bs_buf->size); + return -EINVAL; + } + + /* + * (1) The vp8 bitstream header and body are generated by the HW vp8 + * encoder separately at the same time. We cannot know the bitstream + * header length in advance. + * (2) From the vp8 spec, there is no stuffing byte allowed between the + * ac tag, bitstream header and bitstream body. + */ + memmove(bs_buf->va + bs_hdr_len + ac_tag_size, + bs_buf->va, bs_frm_size); + memcpy(bs_buf->va + ac_tag_size, + inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HEADER].va, + bs_hdr_len); + memcpy(bs_buf->va, ac_tag, ac_tag_size); + *bs_size = bs_frm_size + bs_hdr_len + ac_tag_size; + + return 0; +} + +static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt); + + ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size); + if (ret) + return ret; + + irq_status = vp8_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { + mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + return -EIO; + } + + if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) { + mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed"); + return -EINVAL; + } + + inst->frm_cnt++; + mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size, + inst->vpu_inst.is_key_frm); + + return ret; +} + +static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +{ + int ret = 0; + struct venc_vp8_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; + inst->vpu_inst.id = IPI_VENC_VP8; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + (*handle) = (unsigned long)inst; + + return ret; +} + +static int vp8_enc_encode(unsigned long handle, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_debug_enter(inst); + + enable_irq(ctx->dev->enc_lt_irq); + + switch (opt) { + case VENC_START_OPT_ENCODE_FRAME: + ret = vp8_enc_encode_frame(inst, frm_buf, bs_buf, + &result->bs_size); + if (ret) + goto encode_err; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + + default: + mtk_vcodec_err(inst, "opt not support:%d", opt); + ret = -EINVAL; + break; + } + +encode_err: + + disable_irq(ctx->dev->enc_lt_irq); + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_set_param(unsigned long handle, + enum venc_set_param_type type, + struct venc_enc_param *enc_prm) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + + mtk_vcodec_debug(inst, "->type=%d", type); + + switch (type) { + case VENC_SET_PARAM_ENC: + inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi->config.bitrate = enc_prm->bitrate; + inst->vsi->config.pic_w = enc_prm->width; + inst->vsi->config.pic_h = enc_prm->height; + inst->vsi->config.buf_w = enc_prm->buf_width; + inst->vsi->config.buf_h = enc_prm->buf_height; + inst->vsi->config.gop_size = enc_prm->gop_size; + inst->vsi->config.framerate = enc_prm->frm_rate; + inst->vsi->config.ts_mode = inst->ts_mode; + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + if (ret) + break; + if (inst->work_buf_allocated) { + vp8_enc_free_work_buf(inst); + inst->work_buf_allocated = false; + } + ret = vp8_enc_alloc_work_buf(inst); + if (ret) + break; + inst->work_buf_allocated = true; + break; + + /* + * VENC_SET_PARAM_TS_MODE must be called before VENC_SET_PARAM_ENC + */ + case VENC_SET_PARAM_TS_MODE: + inst->ts_mode = 1; + mtk_vcodec_debug(inst, "set ts_mode"); + break; + + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_deinit(unsigned long handle) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + vp8_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +static struct venc_common_if venc_vp8_if = { + vp8_enc_init, + vp8_enc_encode, + vp8_enc_set_param, + vp8_enc_deinit, +}; + +struct venc_common_if *get_vp8_enc_comm_if(void); + +struct venc_common_if *get_vp8_enc_comm_if(void) +{ + return &venc_vp8_if; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h new file mode 100644 index 000000000000..6308d44dedf6 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_base.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VENC_DRV_BASE_ +#define _VENC_DRV_BASE_ + +#include "mtk_vcodec_drv.h" + +#include "venc_drv_if.h" + +struct venc_common_if { + /** + * (*init)() - initialize driver + * @ctx: [in] mtk v4l2 context + * @handle: [out] driver handle + */ + int (*init)(struct mtk_vcodec_ctx *ctx, unsigned long *handle); + + /** + * (*encode)() - trigger encode + * @handle: [in] driver handle + * @opt: [in] encode option + * @frm_buf: [in] frame buffer to store input frame + * @bs_buf: [in] bitstream buffer to store output bitstream + * @result: [out] encode result + */ + int (*encode)(unsigned long handle, enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + + /** + * (*set_param)() - set driver's parameter + * @handle: [in] driver handle + * @type: [in] parameter type + * @in: [in] buffer to store the parameter + */ + int (*set_param)(unsigned long handle, enum venc_set_param_type type, + struct venc_enc_param *in); + + /** + * (*deinit)() - deinitialize driver. + * @handle: [in] driver handle + */ + int (*deinit)(unsigned long handle); +}; + +#endif diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c new file mode 100644 index 000000000000..c4c83e7189c3 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "venc_drv_base.h" +#include "venc_drv_if.h" + +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vpu.h" + +struct venc_common_if *get_h264_enc_comm_if(void); +struct venc_common_if *get_vp8_enc_comm_if(void); + +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +{ + int ret = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_VP8: + ctx->enc_if = get_vp8_enc_comm_if(); + break; + case V4L2_PIX_FMT_H264: + ctx->enc_if = get_h264_enc_comm_if(); + break; + default: + return -EINVAL; + } + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->init(ctx, (unsigned long *)&ctx->drv_handle); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, struct venc_enc_param *in) +{ + int ret = 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->set_param(ctx->drv_handle, type, in); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + enum venc_start_opt opt, struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + unsigned long flags; + + mtk_venc_lock(ctx); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = ctx; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf, + bs_buf, result); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = NULL; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_venc_unlock(ctx); + return ret; +} + +int venc_if_deinit(struct mtk_vcodec_ctx *ctx) +{ + int ret = 0; + + if (ctx->drv_handle == 0) + return 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->deinit(ctx->drv_handle); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + ctx->drv_handle = 0; + + return ret; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h new file mode 100644 index 000000000000..a6e7d32e55cb --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VENC_DRV_IF_H_ +#define _VENC_DRV_IF_H_ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" + +/* + * enum venc_yuv_fmt - The type of input yuv format + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_YUV_FORMAT_I420: I420 YUV format + * @VENC_YUV_FORMAT_YV12: YV12 YUV format + * @VENC_YUV_FORMAT_NV12: NV12 YUV format + * @VENC_YUV_FORMAT_NV21: NV21 YUV format + */ +enum venc_yuv_fmt { + VENC_YUV_FORMAT_I420 = 3, + VENC_YUV_FORMAT_YV12 = 5, + VENC_YUV_FORMAT_NV12 = 6, + VENC_YUV_FORMAT_NV21 = 7, +}; + +/* + * enum venc_start_opt - encode frame option used in venc_if_encode() + * @VENC_START_OPT_ENCODE_SEQUENCE_HEADER: encode SPS/PPS for H264 + * @VENC_START_OPT_ENCODE_FRAME: encode normal frame + */ +enum venc_start_opt { + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + VENC_START_OPT_ENCODE_FRAME, +}; + +/* + * enum venc_set_param_type - The type of set parameter used in + * venc_if_set_param() + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_SET_PARAM_ENC: set encoder parameters + * @VENC_SET_PARAM_FORCE_INTRA: force an intra frame + * @VENC_SET_PARAM_ADJUST_BITRATE: adjust bitrate (in bps) + * @VENC_SET_PARAM_ADJUST_FRAMERATE: set frame rate + * @VENC_SET_PARAM_GOP_SIZE: set IDR interval + * @VENC_SET_PARAM_INTRA_PERIOD: set I frame interval + * @VENC_SET_PARAM_SKIP_FRAME: set H264 skip one frame + * @VENC_SET_PARAM_PREPEND_HEADER: set H264 prepend SPS/PPS before IDR + * @VENC_SET_PARAM_TS_MODE: set VP8 temporal scalability mode + */ +enum venc_set_param_type { + VENC_SET_PARAM_ENC, + VENC_SET_PARAM_FORCE_INTRA, + VENC_SET_PARAM_ADJUST_BITRATE, + VENC_SET_PARAM_ADJUST_FRAMERATE, + VENC_SET_PARAM_GOP_SIZE, + VENC_SET_PARAM_INTRA_PERIOD, + VENC_SET_PARAM_SKIP_FRAME, + VENC_SET_PARAM_PREPEND_HEADER, + VENC_SET_PARAM_TS_MODE, +}; + +/* + * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in + * venc_if_set_param() + * @input_fourcc: input yuv format + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @width: image width + * @height: image height + * @buf_width: buffer width + * @buf_height: buffer height + * @frm_rate: frame rate in fps + * @intra_period: intra frame period + * @bitrate: target bitrate in bps + * @gop_size: group of picture size + */ +struct venc_enc_param { + enum venc_yuv_fmt input_yuv_fmt; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int width; + unsigned int height; + unsigned int buf_width; + unsigned int buf_height; + unsigned int frm_rate; + unsigned int intra_period; + unsigned int bitrate; + unsigned int gop_size; +}; + +/* + * struct venc_frm_buf - frame buffer information used in venc_if_encode() + * @fb_addr: plane frame buffer addresses + */ +struct venc_frm_buf { + struct mtk_vcodec_mem fb_addr[MTK_VCODEC_MAX_PLANES]; +}; + +/* + * struct venc_done_result - This is return information used in venc_if_encode() + * @bs_size: output bitstream size + * @is_key_frm: output is key frame or not + */ +struct venc_done_result { + unsigned int bs_size; + bool is_key_frm; +}; + +/* + * venc_if_init - Create the driver handle + * @ctx: device context + * @fourcc: encoder input format + * Return: 0 if creating handle successfully, otherwise it is failed. + */ +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); + +/* + * venc_if_deinit - Release the driver handle + * @ctx: device context + * Return: 0 if releasing handle successfully, otherwise it is failed. + */ +int venc_if_deinit(struct mtk_vcodec_ctx *ctx); + +/* + * venc_if_set_param - Set parameter to driver + * @ctx: device context + * @type: parameter type + * @in: input parameter + * Return: 0 if setting param successfully, otherwise it is failed. + */ +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, + struct venc_enc_param *in); + +/* + * venc_if_encode - Encode one frame + * @ctx: device context + * @opt: encode frame option + * @frm_buf: input frame buffer information + * @bs_buf: output bitstream buffer infomraiton + * @result: encode result + * Return: 0 if encoding frame successfully, otherwise it is failed. + */ +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + +#endif /* _VENC_DRV_IF_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h new file mode 100644 index 000000000000..4c869cb6fbf7 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao + * Daniel Hsiao + * Tiffany Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VENC_IPI_MSG_H_ +#define _VENC_IPI_MSG_H_ + +#define AP_IPIMSG_VENC_BASE 0xC000 +#define VPU_IPIMSG_VENC_BASE 0xD000 + +/** + * enum venc_ipi_msg_id - message id between AP and VPU + * (ipi stands for inter-processor interrupt) + * @AP_IPIMSG_ENC_XXX: AP to VPU cmd message id + * @VPU_IPIMSG_ENC_XXX_DONE: VPU ack AP cmd message id + */ +enum venc_ipi_msg_id { + AP_IPIMSG_ENC_INIT = AP_IPIMSG_VENC_BASE, + AP_IPIMSG_ENC_SET_PARAM, + AP_IPIMSG_ENC_ENCODE, + AP_IPIMSG_ENC_DEINIT, + + VPU_IPIMSG_ENC_INIT_DONE = VPU_IPIMSG_VENC_BASE, + VPU_IPIMSG_ENC_SET_PARAM_DONE, + VPU_IPIMSG_ENC_ENCODE_DONE, + VPU_IPIMSG_ENC_DEINIT_DONE, +}; + +/** + * struct venc_ap_ipi_msg_init - AP to VPU init cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_INIT) + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + * @venc_inst: AP encoder instance + * (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_ap_ipi_msg_init { + uint32_t msg_id; + uint32_t reserved; + uint64_t venc_inst; +}; + +/** + * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_SET_PARAM) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data[8]: data array to store the set parameters + */ +struct venc_ap_ipi_msg_set_param { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t param_id; + uint32_t data_item; + uint32_t data[8]; +}; + +/** + * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @bs_mode: bitstream mode for h264 + * (H264_BS_MODE_SPS/H264_BS_MODE_PPS/H264_BS_MODE_FRAME) + * @input_addr: pointer to input image buffer plane + * @bs_addr: pointer to output bit stream buffer + * @bs_size: bit stream buffer size + */ +struct venc_ap_ipi_msg_enc { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t bs_mode; + uint32_t input_addr[3]; + uint32_t bs_addr; + uint32_t bs_size; +}; + +/** + * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + */ +struct venc_ap_ipi_msg_deinit { + uint32_t msg_id; + uint32_t vpu_inst_addr; +}; + +/** + * enum venc_ipi_msg_status - VPU ack AP cmd status + */ +enum venc_ipi_msg_status { + VENC_IPI_MSG_STATUS_OK, + VENC_IPI_MSG_STATUS_FAIL, +}; + +/** + * struct venc_vpu_ipi_msg_common - VPU ack AP cmd common structure + * @msg_id: message id (VPU_IPIMSG_XXX_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_common { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +/** + * struct venc_vpu_ipi_msg_init - VPU ack AP init cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + */ +struct venc_vpu_ipi_msg_init { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t vpu_inst_addr; + uint32_t reserved; +}; + +/** + * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data[6]: data array to store the return result + */ +struct venc_vpu_ipi_msg_set_param { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t param_id; + uint32_t data_item; + uint32_t data[6]; +}; + +/** + * enum venc_ipi_msg_enc_state - Type of encode state + * VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded + * VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full + * VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame + * VEN_IPI_MSG_ENC_STATE_ERROR: encounter error + */ +enum venc_ipi_msg_enc_state { + VEN_IPI_MSG_ENC_STATE_FRAME, + VEN_IPI_MSG_ENC_STATE_PART, + VEN_IPI_MSG_ENC_STATE_SKIP, + VEN_IPI_MSG_ENC_STATE_ERROR, +}; + +/** + * struct venc_vpu_ipi_msg_enc - VPU ack AP enc cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_ENCODE_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @state: encode state (venc_ipi_msg_enc_state) + * @is_key_frm: whether the encoded frame is key frame + * @bs_size: encoded bitstream size + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + */ +struct venc_vpu_ipi_msg_enc { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t state; + uint32_t is_key_frm; + uint32_t bs_size; + uint32_t reserved; +}; + +/** + * struct venc_vpu_ipi_msg_deinit - VPU ack AP deinit cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_DEINIT_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_deinit { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +#endif /* _VENC_IPI_MSG_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c new file mode 100644 index 000000000000..a01c7599b510 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mtk_vpu.h" +#include "venc_ipi_msg.h" +#include "venc_vpu_if.h" + +static void handle_enc_init_msg(struct venc_vpu_inst *vpu, void *data) +{ + struct venc_vpu_ipi_msg_init *msg = data; + + vpu->inst_addr = msg->vpu_inst_addr; + vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr); +} + +static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, void *data) +{ + struct venc_vpu_ipi_msg_enc *msg = data; + + vpu->state = msg->state; + vpu->bs_size = msg->bs_size; + vpu->is_key_frm = msg->is_key_frm; +} + +static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct venc_vpu_ipi_msg_common *msg = data; + struct venc_vpu_inst *vpu = + (struct venc_vpu_inst *)(unsigned long)msg->venc_inst; + + mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d", + msg->msg_id, vpu, msg->status); + + switch (msg->msg_id) { + case VPU_IPIMSG_ENC_INIT_DONE: + handle_enc_init_msg(vpu, data); + break; + case VPU_IPIMSG_ENC_SET_PARAM_DONE: + break; + case VPU_IPIMSG_ENC_ENCODE_DONE: + handle_enc_encode_msg(vpu, data); + break; + case VPU_IPIMSG_ENC_DEINIT_DONE: + break; + default: + mtk_vcodec_err(vpu, "unknown msg id %x", msg->msg_id); + break; + } + + vpu->signaled = 1; + vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); + + mtk_vcodec_debug_leave(vpu); +} + +static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, + int len) +{ + int status; + + mtk_vcodec_debug_enter(vpu); + + if (!vpu->dev) { + mtk_vcodec_err(vpu, "inst dev is NULL"); + return -EINVAL; + } + + status = vpu_ipi_send(vpu->dev, vpu->id, msg, len); + if (status) { + uint32_t msg_id = *(uint32_t *)msg; + + mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", + msg_id, len, status); + return -EINVAL; + } + if (vpu->failure) + return -EINVAL; + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +int vpu_enc_init(struct venc_vpu_inst *vpu) +{ + int status; + struct venc_ap_ipi_msg_init out; + + mtk_vcodec_debug_enter(vpu); + + init_waitqueue_head(&vpu->wq_hd); + vpu->signaled = 0; + vpu->failure = 0; + + status = vpu_ipi_register(vpu->dev, vpu->id, vpu_enc_ipi_handler, + NULL, NULL); + if (status) { + mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); + return -EINVAL; + } + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_INIT; + out.venc_inst = (unsigned long)vpu; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *enc_param) +{ + struct venc_ap_ipi_msg_set_param out; + + mtk_vcodec_debug(vpu, "id %d ->", id); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_SET_PARAM; + out.vpu_inst_addr = vpu->inst_addr; + out.param_id = id; + switch (id) { + case VENC_SET_PARAM_ENC: + out.data_item = 0; + break; + case VENC_SET_PARAM_FORCE_INTRA: + out.data_item = 0; + break; + case VENC_SET_PARAM_ADJUST_BITRATE: + out.data_item = 1; + out.data[0] = enc_param->bitrate; + break; + case VENC_SET_PARAM_ADJUST_FRAMERATE: + out.data_item = 1; + out.data[0] = enc_param->frm_rate; + break; + case VENC_SET_PARAM_GOP_SIZE: + out.data_item = 1; + out.data[0] = enc_param->gop_size; + break; + case VENC_SET_PARAM_INTRA_PERIOD: + out.data_item = 1; + out.data[0] = enc_param->intra_period; + break; + case VENC_SET_PARAM_SKIP_FRAME: + out.data_item = 0; + break; + default: + mtk_vcodec_err(vpu, "id %d not supported", id); + return -EINVAL; + } + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, + "AP_IPIMSG_ENC_SET_PARAM %d fail", id); + return -EINVAL; + } + + mtk_vcodec_debug(vpu, "id %d <-", id); + + return 0; +} + +int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + struct venc_ap_ipi_msg_enc out; + + mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_ENCODE; + out.vpu_inst_addr = vpu->inst_addr; + out.bs_mode = bs_mode; + if (frm_buf) { + if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) && + (frm_buf->fb_addr[1].dma_addr % 16 == 0) && + (frm_buf->fb_addr[2].dma_addr % 16 == 0)) { + out.input_addr[0] = frm_buf->fb_addr[0].dma_addr; + out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; + out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; + } else { + mtk_vcodec_err(vpu, "dma_addr not align to 16"); + return -EINVAL; + } + } + if (bs_buf) { + out.bs_addr = bs_buf->dma_addr; + out.bs_size = bs_buf->size; + } + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", + bs_mode); + return -EINVAL; + } + + mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-", + bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); + + return 0; +} + +int vpu_enc_deinit(struct venc_vpu_inst *vpu) +{ + struct venc_ap_ipi_msg_deinit out; + + mtk_vcodec_debug_enter(vpu); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_DEINIT; + out.vpu_inst_addr = vpu->inst_addr; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h new file mode 100644 index 000000000000..215d1e01362e --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin + * + * This program is free software; you can redistribute it and/or + * modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VENC_VPU_IF_H_ +#define _VENC_VPU_IF_H_ + +#include "mtk_vpu.h" +#include "venc_drv_if.h" + +/* + * struct venc_vpu_inst - encoder VPU driver instance + * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done + * @signaled: flag used for checking vpu interrupt done + * @failure: flag to show vpu cmd succeeds or not + * @state: enum venc_ipi_msg_enc_state + * @bs_size: bitstream size for skip frame case usage + * @is_key_frm: key frame flag + * @inst_addr: VPU instance addr + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @id: the id of inter-processor interrupt + * @ctx: context for v4l2 layer integration + * @dev: device for v4l2 layer integration + */ +struct venc_vpu_inst { + wait_queue_head_t wq_hd; + int signaled; + int failure; + int state; + int bs_size; + int is_key_frm; + unsigned int inst_addr; + void *vsi; + enum ipi_id id; + struct mtk_vcodec_ctx *ctx; + struct platform_device *dev; +}; + +int vpu_enc_init(struct venc_vpu_inst *vpu); +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *param); +int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size); +int vpu_enc_deinit(struct venc_vpu_inst *vpu); + +#endif diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mtk-vpu/Makefile new file mode 100644 index 000000000000..58cc1b4bc9f2 --- /dev/null +++ b/drivers/media/platform/mtk-vpu/Makefile @@ -0,0 +1,3 @@ +mtk-vpu-y += mtk_vpu.o + +obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu.o diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c new file mode 100644 index 000000000000..c9bf58c97878 --- /dev/null +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -0,0 +1,946 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Andrew-CT Chen +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vpu.h" + +/** + * VPU (video processor unit) is a tiny processor controlling video hardware + * related to video codec, scaling and color format converting. + * VPU interfaces with other blocks by share memory and interrupt. + **/ + +#define INIT_TIMEOUT_MS 2000U +#define IPI_TIMEOUT_MS 2000U +#define VPU_FW_VER_LEN 16 + +/* maximum program/data TCM (Tightly-Coupled Memory) size */ +#define VPU_PTCM_SIZE (96 * SZ_1K) +#define VPU_DTCM_SIZE (32 * SZ_1K) +/* the offset to get data tcm address */ +#define VPU_DTCM_OFFSET 0x18000UL +/* daynamic allocated maximum extended memory size */ +#define VPU_EXT_P_SIZE SZ_1M +#define VPU_EXT_D_SIZE SZ_4M +/* maximum binary firmware size */ +#define VPU_P_FW_SIZE (VPU_PTCM_SIZE + VPU_EXT_P_SIZE) +#define VPU_D_FW_SIZE (VPU_DTCM_SIZE + VPU_EXT_D_SIZE) +/* the size of share buffer between Host and VPU */ +#define SHARE_BUF_SIZE 48 + +/* binary firmware name */ +#define VPU_P_FW "vpu_p.bin" +#define VPU_D_FW "vpu_d.bin" + +#define VPU_RESET 0x0 +#define VPU_TCM_CFG 0x0008 +#define VPU_PMEM_EXT0_ADDR 0x000C +#define VPU_PMEM_EXT1_ADDR 0x0010 +#define VPU_TO_HOST 0x001C +#define VPU_DMEM_EXT0_ADDR 0x0014 +#define VPU_DMEM_EXT1_ADDR 0x0018 +#define HOST_TO_VPU 0x0024 +#define VPU_PC_REG 0x0060 +#define VPU_WDT_REG 0x0084 + +/* vpu inter-processor communication interrupt */ +#define VPU_IPC_INT BIT(8) + +/** + * enum vpu_fw_type - VPU firmware type + * + * @P_FW: program firmware + * @D_FW: data firmware + * + */ +enum vpu_fw_type { + P_FW, + D_FW, +}; + +/** + * struct vpu_mem - VPU extended program/data memory information + * + * @va: the kernel virtual memory address of VPU extended memory + * @pa: the physical memory address of VPU extended memory + * + */ +struct vpu_mem { + void *va; + dma_addr_t pa; +}; + +/** + * struct vpu_regs - VPU TCM and configuration registers + * + * @tcm: the register for VPU Tightly-Coupled Memory + * @cfg: the register for VPU configuration + * @irq: the irq number for VPU interrupt + */ +struct vpu_regs { + void __iomem *tcm; + void __iomem *cfg; + int irq; +}; + +/** + * struct vpu_wdt_handler - VPU watchdog reset handler + * + * @reset_func: reset handler + * @priv: private data + */ +struct vpu_wdt_handler { + void (*reset_func)(void *); + void *priv; +}; + +/** + * struct vpu_wdt - VPU watchdog workqueue + * + * @handler: VPU watchdog reset handler + * @ws: workstruct for VPU watchdog + * @wq: workqueue for VPU watchdog + */ +struct vpu_wdt { + struct vpu_wdt_handler handler[VPU_RST_MAX]; + struct work_struct ws; + struct workqueue_struct *wq; +}; + +/** + * struct vpu_run - VPU initialization status + * + * @signaled: the signal of vpu initialization completed + * @fw_ver: VPU firmware version + * @enc_capability: encoder capability which is not used for now and + * the value is reserved for future use + * @wq: wait queue for VPU initialization status + */ +struct vpu_run { + u32 signaled; + char fw_ver[VPU_FW_VER_LEN]; + unsigned int enc_capability; + wait_queue_head_t wq; +}; + +/** + * struct vpu_ipi_desc - VPU IPI descriptor + * + * @handler: IPI handler + * @name: the name of IPI handler + * @priv: the private data of IPI handler + */ +struct vpu_ipi_desc { + ipi_handler_t handler; + const char *name; + void *priv; +}; + +/** + * struct share_obj - DTCM (Data Tightly-Coupled Memory) buffer shared with + * AP and VPU + * + * @id: IPI id + * @len: share buffer length + * @share_buf: share buffer data + */ +struct share_obj { + s32 id; + u32 len; + unsigned char share_buf[SHARE_BUF_SIZE]; +}; + +/** + * struct mtk_vpu - vpu driver data + * @extmem: VPU extended memory information + * @reg: VPU TCM and configuration registers + * @run: VPU initialization status + * @ipi_desc: VPU IPI descriptor + * @recv_buf: VPU DTCM share buffer for receiving. The + * receive buffer is only accessed in interrupt context. + * @send_buf: VPU DTCM share buffer for sending + * @dev: VPU struct device + * @clk: VPU clock on/off + * @fw_loaded: indicate VPU firmware loaded + * @enable_4GB: VPU 4GB mode on/off + * @vpu_mutex: protect mtk_vpu (except recv_buf) and ensure only + * one client to use VPU service at a time. For example, + * suppose a client is using VPU to decode VP8. + * If the other client wants to encode VP8, + * it has to wait until VP8 decode completes. + * @wdt_refcnt WDT reference count to make sure the watchdog can be + * disabled if no other client is using VPU service + * @ack_wq: The wait queue for each codec and mdp. When sleeping + * processes wake up, they will check the condition + * "ipi_id_ack" to run the corresponding action or + * go back to sleep. + * @ipi_id_ack: The ACKs for registered IPI function sending + * interrupt to VPU + * + */ +struct mtk_vpu { + struct vpu_mem extmem[2]; + struct vpu_regs reg; + struct vpu_run run; + struct vpu_wdt wdt; + struct vpu_ipi_desc ipi_desc[IPI_MAX]; + struct share_obj *recv_buf; + struct share_obj *send_buf; + struct device *dev; + struct clk *clk; + bool fw_loaded; + bool enable_4GB; + struct mutex vpu_mutex; /* for protecting vpu data data structure */ + u32 wdt_refcnt; + wait_queue_head_t ack_wq; + bool ipi_id_ack[IPI_MAX]; +}; + +static inline void vpu_cfg_writel(struct mtk_vpu *vpu, u32 val, u32 offset) +{ + writel(val, vpu->reg.cfg + offset); +} + +static inline u32 vpu_cfg_readl(struct mtk_vpu *vpu, u32 offset) +{ + return readl(vpu->reg.cfg + offset); +} + +static inline bool vpu_running(struct mtk_vpu *vpu) +{ + return vpu_cfg_readl(vpu, VPU_RESET) & BIT(0); +} + +static void vpu_clock_disable(struct mtk_vpu *vpu) +{ + /* Disable VPU watchdog */ + mutex_lock(&vpu->vpu_mutex); + if (!--vpu->wdt_refcnt) + vpu_cfg_writel(vpu, + vpu_cfg_readl(vpu, VPU_WDT_REG) & ~(1L << 31), + VPU_WDT_REG); + mutex_unlock(&vpu->vpu_mutex); + + clk_disable(vpu->clk); +} + +static int vpu_clock_enable(struct mtk_vpu *vpu) +{ + int ret; + + ret = clk_enable(vpu->clk); + if (ret) + return ret; + /* Enable VPU watchdog */ + mutex_lock(&vpu->vpu_mutex); + if (!vpu->wdt_refcnt++) + vpu_cfg_writel(vpu, + vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31), + VPU_WDT_REG); + mutex_unlock(&vpu->vpu_mutex); + + return ret; +} + +int vpu_ipi_register(struct platform_device *pdev, + enum ipi_id id, ipi_handler_t handler, + const char *name, void *priv) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct vpu_ipi_desc *ipi_desc; + + if (!vpu) { + dev_err(&pdev->dev, "vpu device in not ready\n"); + return -EPROBE_DEFER; + } + + if (id >= 0 && id < IPI_MAX && handler) { + ipi_desc = vpu->ipi_desc; + ipi_desc[id].name = name; + ipi_desc[id].handler = handler; + ipi_desc[id].priv = priv; + return 0; + } + + dev_err(&pdev->dev, "register vpu ipi id %d with invalid arguments\n", + id); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vpu_ipi_register); + +int vpu_ipi_send(struct platform_device *pdev, + enum ipi_id id, void *buf, + unsigned int len) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct share_obj *send_obj = vpu->send_buf; + unsigned long timeout; + int ret = 0; + + if (id <= IPI_VPU_INIT || id >= IPI_MAX || + len > sizeof(send_obj->share_buf) || !buf) { + dev_err(vpu->dev, "failed to send ipi message\n"); + return -EINVAL; + } + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "failed to enable vpu clock\n"); + return ret; + } + if (!vpu_running(vpu)) { + dev_err(vpu->dev, "vpu_ipi_send: VPU is not running\n"); + ret = -EINVAL; + goto clock_disable; + } + + mutex_lock(&vpu->vpu_mutex); + + /* Wait until VPU receives the last command */ + timeout = jiffies + msecs_to_jiffies(IPI_TIMEOUT_MS); + do { + if (time_after(jiffies, timeout)) { + dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n"); + ret = -EIO; + goto mut_unlock; + } + } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); + + memcpy((void *)send_obj->share_buf, buf, len); + send_obj->len = len; + send_obj->id = id; + + vpu->ipi_id_ack[id] = false; + /* send the command to VPU */ + vpu_cfg_writel(vpu, 0x1, HOST_TO_VPU); + + mutex_unlock(&vpu->vpu_mutex); + + /* wait for VPU's ACK */ + timeout = msecs_to_jiffies(IPI_TIMEOUT_MS); + ret = wait_event_timeout(vpu->ack_wq, vpu->ipi_id_ack[id], timeout); + vpu->ipi_id_ack[id] = false; + if (ret == 0) { + dev_err(vpu->dev, "vpu ipi %d ack time out !", id); + ret = -EIO; + goto clock_disable; + } + vpu_clock_disable(vpu); + + return 0; + +mut_unlock: + mutex_unlock(&vpu->vpu_mutex); +clock_disable: + vpu_clock_disable(vpu); + + return ret; +} +EXPORT_SYMBOL_GPL(vpu_ipi_send); + +static void vpu_wdt_reset_func(struct work_struct *ws) +{ + struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws); + struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt); + struct vpu_wdt_handler *handler = wdt->handler; + int index, ret; + + dev_info(vpu->dev, "vpu reset\n"); + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret); + return; + } + mutex_lock(&vpu->vpu_mutex); + vpu_cfg_writel(vpu, 0x0, VPU_RESET); + vpu->fw_loaded = false; + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + + for (index = 0; index < VPU_RST_MAX; index++) { + if (handler[index].reset_func) { + handler[index].reset_func(handler[index].priv); + dev_dbg(vpu->dev, "wdt handler func %d\n", index); + } + } +} + +int vpu_wdt_reg_handler(struct platform_device *pdev, + void wdt_reset(void *), + void *priv, enum rst_id id) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct vpu_wdt_handler *handler; + + if (!vpu) { + dev_err(&pdev->dev, "vpu device in not ready\n"); + return -EPROBE_DEFER; + } + + handler = vpu->wdt.handler; + + if (id >= 0 && id < VPU_RST_MAX && wdt_reset) { + dev_dbg(vpu->dev, "wdt register id %d\n", id); + mutex_lock(&vpu->vpu_mutex); + handler[id].reset_func = wdt_reset; + handler[id].priv = priv; + mutex_unlock(&vpu->vpu_mutex); + return 0; + } + + dev_err(vpu->dev, "register vpu wdt handler failed\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vpu_wdt_reg_handler); + +unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + + return vpu->run.enc_capability; +} +EXPORT_SYMBOL_GPL(vpu_get_venc_hw_capa); + +void *vpu_mapping_dm_addr(struct platform_device *pdev, + u32 dtcm_dmem_addr) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + + if (!dtcm_dmem_addr || + (dtcm_dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) { + dev_err(vpu->dev, "invalid virtual data memory address\n"); + return ERR_PTR(-EINVAL); + } + + if (dtcm_dmem_addr < VPU_DTCM_SIZE) + return (__force void *)(dtcm_dmem_addr + vpu->reg.tcm + + VPU_DTCM_OFFSET); + + return vpu->extmem[D_FW].va + (dtcm_dmem_addr - VPU_DTCM_SIZE); +} +EXPORT_SYMBOL_GPL(vpu_mapping_dm_addr); + +struct platform_device *vpu_get_plat_device(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *vpu_node; + struct platform_device *vpu_pdev; + + vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0); + if (!vpu_node) { + dev_err(dev, "can't get vpu node\n"); + return NULL; + } + + vpu_pdev = of_find_device_by_node(vpu_node); + if (WARN_ON(!vpu_pdev)) { + dev_err(dev, "vpu pdev failed\n"); + of_node_put(vpu_node); + return NULL; + } + + return vpu_pdev; +} +EXPORT_SYMBOL_GPL(vpu_get_plat_device); + +/* load vpu program/data memory */ +static int load_requested_vpu(struct mtk_vpu *vpu, + const struct firmware *vpu_fw, + u8 fw_type) +{ + size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE; + size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE; + char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW; + size_t dl_size = 0; + size_t extra_fw_size = 0; + void *dest; + int ret; + + ret = request_firmware(&vpu_fw, fw_name, vpu->dev); + if (ret < 0) { + dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret); + return ret; + } + dl_size = vpu_fw->size; + if (dl_size > fw_size) { + dev_err(vpu->dev, "fw %s size %zu is abnormal\n", fw_name, + dl_size); + release_firmware(vpu_fw); + return -EFBIG; + } + dev_dbg(vpu->dev, "Downloaded fw %s size: %zu.\n", + fw_name, + dl_size); + /* reset VPU */ + vpu_cfg_writel(vpu, 0x0, VPU_RESET); + + /* handle extended firmware size */ + if (dl_size > tcm_size) { + dev_dbg(vpu->dev, "fw size %zu > limited fw size %zu\n", + dl_size, tcm_size); + extra_fw_size = dl_size - tcm_size; + dev_dbg(vpu->dev, "extra_fw_size %zu\n", extra_fw_size); + dl_size = tcm_size; + } + dest = (__force void *)vpu->reg.tcm; + if (fw_type == D_FW) + dest += VPU_DTCM_OFFSET; + memcpy(dest, vpu_fw->data, dl_size); + /* download to extended memory if need */ + if (extra_fw_size > 0) { + dest = vpu->extmem[fw_type].va; + dev_dbg(vpu->dev, "download extended memory type %x\n", + fw_type); + memcpy(dest, vpu_fw->data + tcm_size, extra_fw_size); + } + + release_firmware(vpu_fw); + + return 0; +} + +int vpu_load_firmware(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct vpu_run *run = &vpu->run; + const struct firmware *vpu_fw = NULL; + int ret; + + if (!pdev) { + dev_err(dev, "VPU platform device is invalid\n"); + return -EINVAL; + } + + mutex_lock(&vpu->vpu_mutex); + if (vpu->fw_loaded) { + mutex_unlock(&vpu->vpu_mutex); + return 0; + } + mutex_unlock(&vpu->vpu_mutex); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "enable clock failed %d\n", ret); + return ret; + } + + mutex_lock(&vpu->vpu_mutex); + + run->signaled = false; + dev_dbg(vpu->dev, "firmware request\n"); + /* Downloading program firmware to device*/ + ret = load_requested_vpu(vpu, vpu_fw, P_FW); + if (ret < 0) { + dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret); + goto OUT_LOAD_FW; + } + + /* Downloading data firmware to device */ + ret = load_requested_vpu(vpu, vpu_fw, D_FW); + if (ret < 0) { + dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret); + goto OUT_LOAD_FW; + } + + vpu->fw_loaded = true; + /* boot up vpu */ + vpu_cfg_writel(vpu, 0x1, VPU_RESET); + + ret = wait_event_interruptible_timeout(run->wq, + run->signaled, + msecs_to_jiffies(INIT_TIMEOUT_MS) + ); + if (ret == 0) { + ret = -ETIME; + dev_err(dev, "wait vpu initialization timout!\n"); + goto OUT_LOAD_FW; + } else if (-ERESTARTSYS == ret) { + dev_err(dev, "wait vpu interrupted by a signal!\n"); + goto OUT_LOAD_FW; + } + + ret = 0; + dev_info(dev, "vpu is ready. Fw version %s\n", run->fw_ver); + +OUT_LOAD_FW: + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + + return ret; +} +EXPORT_SYMBOL_GPL(vpu_load_firmware); + +static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct mtk_vpu *vpu = (struct mtk_vpu *)priv; + struct vpu_run *run = (struct vpu_run *)data; + + vpu->run.signaled = run->signaled; + strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN); + vpu->run.enc_capability = run->enc_capability; + wake_up_interruptible(&vpu->run.wq); +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[256]; + unsigned int len; + unsigned int running, pc, vpu_to_host, host_to_vpu, wdt; + int ret; + struct device *dev = file->private_data; + struct mtk_vpu *vpu = dev_get_drvdata(dev); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); + return 0; + } + + /* vpu register status */ + running = vpu_running(vpu); + pc = vpu_cfg_readl(vpu, VPU_PC_REG); + wdt = vpu_cfg_readl(vpu, VPU_WDT_REG); + host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU); + vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); + vpu_clock_disable(vpu); + + if (running) { + len = snprintf(buf, sizeof(buf), "VPU is running\n\n" + "FW Version: %s\n" + "PC: 0x%x\n" + "WDT: 0x%x\n" + "Host to VPU: 0x%x\n" + "VPU to Host: 0x%x\n", + vpu->run.fw_ver, pc, wdt, + host_to_vpu, vpu_to_host); + } else { + len = snprintf(buf, sizeof(buf), "VPU not running\n"); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations vpu_debug_fops = { + .open = simple_open, + .read = vpu_debug_read, +}; +#endif /* CONFIG_DEBUG_FS */ + +static void vpu_free_ext_mem(struct mtk_vpu *vpu, u8 fw_type) +{ + struct device *dev = vpu->dev; + size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; + + dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va, + vpu->extmem[fw_type].pa); +} + +static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type) +{ + struct device *dev = vpu->dev; + size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; + u32 vpu_ext_mem0 = fw_type ? VPU_DMEM_EXT0_ADDR : VPU_PMEM_EXT0_ADDR; + u32 vpu_ext_mem1 = fw_type ? VPU_DMEM_EXT1_ADDR : VPU_PMEM_EXT1_ADDR; + u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0; + + vpu->extmem[fw_type].va = dma_alloc_coherent(dev, + fw_ext_size, + &vpu->extmem[fw_type].pa, + GFP_KERNEL); + if (!vpu->extmem[fw_type].va) { + dev_err(dev, "Failed to allocate the extended program memory\n"); + return PTR_ERR(vpu->extmem[fw_type].va); + } + + /* Disable extend0. Enable extend1 */ + vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0); + vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb, + vpu_ext_mem1); + + dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n", + fw_type ? "Data" : "Program", + (unsigned long long)vpu->extmem[fw_type].pa, + vpu->extmem[fw_type].va); + + return 0; +} + +static void vpu_ipi_handler(struct mtk_vpu *vpu) +{ + struct share_obj *rcv_obj = vpu->recv_buf; + struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc; + + if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) { + ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf, + rcv_obj->len, + ipi_desc[rcv_obj->id].priv); + if (rcv_obj->id > IPI_VPU_INIT) { + vpu->ipi_id_ack[rcv_obj->id] = true; + wake_up(&vpu->ack_wq); + } + } else { + dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id); + } +} + +static int vpu_ipi_init(struct mtk_vpu *vpu) +{ + /* Disable VPU to host interrupt */ + vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); + + /* shared buffer initialization */ + vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm + + VPU_DTCM_OFFSET); + vpu->send_buf = vpu->recv_buf + 1; + memset(vpu->recv_buf, 0, sizeof(struct share_obj)); + memset(vpu->send_buf, 0, sizeof(struct share_obj)); + + return 0; +} + +static irqreturn_t vpu_irq_handler(int irq, void *priv) +{ + struct mtk_vpu *vpu = priv; + u32 vpu_to_host; + int ret; + + /* + * Clock should have been enabled already. + * Enable again in case vpu_ipi_send times out + * and has disabled the clock. + */ + ret = clk_enable(vpu->clk); + if (ret) { + dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); + return IRQ_NONE; + } + vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); + if (vpu_to_host & VPU_IPC_INT) { + vpu_ipi_handler(vpu); + } else { + dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host); + queue_work(vpu->wdt.wq, &vpu->wdt.ws); + } + + /* VPU won't send another interrupt until we set VPU_TO_HOST to 0. */ + vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); + clk_disable(vpu->clk); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *vpu_debugfs; +#endif +static int mtk_vpu_probe(struct platform_device *pdev) +{ + struct mtk_vpu *vpu; + struct device *dev; + struct resource *res; + int ret = 0; + + dev_dbg(&pdev->dev, "initialization\n"); + + dev = &pdev->dev; + vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL); + if (!vpu) + return -ENOMEM; + + vpu->dev = &pdev->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcm"); + vpu->reg.tcm = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)vpu->reg.tcm)) + return PTR_ERR((__force void *)vpu->reg.tcm); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_reg"); + vpu->reg.cfg = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)vpu->reg.cfg)) + return PTR_ERR((__force void *)vpu->reg.cfg); + + /* Get VPU clock */ + vpu->clk = devm_clk_get(dev, "main"); + if (IS_ERR(vpu->clk)) { + dev_err(dev, "get vpu clock failed\n"); + return PTR_ERR(vpu->clk); + } + + platform_set_drvdata(pdev, vpu); + + ret = clk_prepare(vpu->clk); + if (ret) { + dev_err(dev, "prepare vpu clock failed\n"); + return ret; + } + + /* VPU watchdog */ + vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt"); + if (!vpu->wdt.wq) { + dev_err(dev, "initialize wdt workqueue failed\n"); + return -ENOMEM; + } + INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func); + mutex_init(&vpu->vpu_mutex); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "enable vpu clock failed\n"); + goto workqueue_destroy; + } + + dev_dbg(dev, "vpu ipi init\n"); + ret = vpu_ipi_init(vpu); + if (ret) { + dev_err(dev, "Failed to init ipi\n"); + goto disable_vpu_clk; + } + + /* register vpu initialization IPI */ + ret = vpu_ipi_register(pdev, IPI_VPU_INIT, vpu_init_ipi_handler, + "vpu_init", vpu); + if (ret) { + dev_err(dev, "Failed to register IPI_VPU_INIT\n"); + goto vpu_mutex_destroy; + } + +#ifdef CONFIG_DEBUG_FS + vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev, + &vpu_debug_fops); + if (!vpu_debugfs) { + ret = -ENOMEM; + goto cleanup_ipi; + } +#endif + + /* Set PTCM to 96K and DTCM to 32K */ + vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG); + + vpu->enable_4GB = !!(totalram_pages > (SZ_2G >> PAGE_SHIFT)); + dev_info(dev, "4GB mode %u\n", vpu->enable_4GB); + + if (vpu->enable_4GB) { + ret = of_reserved_mem_device_init(dev); + if (ret) + dev_info(dev, "init reserved memory failed\n"); + /* continue to use dynamic allocation if failed */ + } + + ret = vpu_alloc_ext_mem(vpu, D_FW); + if (ret) { + dev_err(dev, "Allocate DM failed\n"); + goto remove_debugfs; + } + + ret = vpu_alloc_ext_mem(vpu, P_FW); + if (ret) { + dev_err(dev, "Allocate PM failed\n"); + goto free_d_mem; + } + + init_waitqueue_head(&vpu->run.wq); + init_waitqueue_head(&vpu->ack_wq); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "get IRQ resource failed.\n"); + ret = -ENXIO; + goto free_p_mem; + } + vpu->reg.irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0, + pdev->name, vpu); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto free_p_mem; + } + + vpu_clock_disable(vpu); + dev_dbg(dev, "initialization completed\n"); + + return 0; + +free_p_mem: + vpu_free_ext_mem(vpu, P_FW); +free_d_mem: + vpu_free_ext_mem(vpu, D_FW); +remove_debugfs: + of_reserved_mem_device_release(dev); +#ifdef CONFIG_DEBUG_FS + debugfs_remove(vpu_debugfs); +cleanup_ipi: +#endif + memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX); +vpu_mutex_destroy: + mutex_destroy(&vpu->vpu_mutex); +disable_vpu_clk: + vpu_clock_disable(vpu); +workqueue_destroy: + destroy_workqueue(vpu->wdt.wq); + + return ret; +} + +static const struct of_device_id mtk_vpu_match[] = { + { + .compatible = "mediatek,mt8173-vpu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vpu_match); + +static int mtk_vpu_remove(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove(vpu_debugfs); +#endif + if (vpu->wdt.wq) { + flush_workqueue(vpu->wdt.wq); + destroy_workqueue(vpu->wdt.wq); + } + vpu_free_ext_mem(vpu, P_FW); + vpu_free_ext_mem(vpu, D_FW); + mutex_destroy(&vpu->vpu_mutex); + clk_unprepare(vpu->clk); + + return 0; +} + +static struct platform_driver mtk_vpu_driver = { + .probe = mtk_vpu_probe, + .remove = mtk_vpu_remove, + .driver = { + .name = "mtk_vpu", + .of_match_table = mtk_vpu_match, + }, +}; + +module_platform_driver(mtk_vpu_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver"); diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h new file mode 100644 index 000000000000..5ab37f04bdfd --- /dev/null +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Andrew-CT Chen +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _MTK_VPU_H +#define _MTK_VPU_H + +#include + +/** + * VPU (video processor unit) is a tiny processor controlling video hardware + * related to video codec, scaling and color format converting. + * VPU interfaces with other blocks by share memory and interrupt. + **/ + +typedef void (*ipi_handler_t) (void *data, + unsigned int len, + void *priv); + +/** + * enum ipi_id - the id of inter-processor interrupt + * + * @IPI_VPU_INIT: The interrupt from vpu is to notfiy kernel + VPU initialization completed. + IPI_VPU_INIT is sent from VPU when firmware is + loaded. AP doesn't need to send IPI_VPU_INIT + command to VPU. + For other IPI below, AP should send the request + to VPU to trigger the interrupt. + * @IPI_VENC_H264: The interrupt from vpu is to notify kernel to + handle H264 video encoder job, and vice versa. + * @IPI_VENC_VP8: The interrupt fro vpu is to notify kernel to + handle VP8 video encoder job,, and vice versa. + * @IPI_MAX: The maximum IPI number + */ + +enum ipi_id { + IPI_VPU_INIT = 0, + IPI_VENC_H264, + IPI_VENC_VP8, + IPI_MAX, +}; + +/** + * enum rst_id - reset id to register reset function for VPU watchdog timeout + * + * @VPU_RST_ENC: encoder reset id + * @VPU_RST_MAX: maximum reset id + */ +enum rst_id { + VPU_RST_ENC, + VPU_RST_MAX, +}; + +/** + * vpu_ipi_register - register an ipi function + * + * @pdev: VPU platform device + * @id: IPI ID + * @handler: IPI handler + * @name: IPI name + * @priv: private data for IPI handler + * + * Register an ipi function to receive ipi interrupt from VPU. + * + * Return: Return 0 if ipi registers successfully, otherwise it is failed. + */ +int vpu_ipi_register(struct platform_device *pdev, enum ipi_id id, + ipi_handler_t handler, const char *name, void *priv); + +/** + * vpu_ipi_send - send data from AP to vpu. + * + * @pdev: VPU platform device + * @id: IPI ID + * @buf: the data buffer + * @len: the data buffer length + * + * This function is thread-safe. When this function returns, + * VPU has received the data and starts the processing. + * When the processing completes, IPI handler registered + * by vpu_ipi_register will be called in interrupt context. + * + * Return: Return 0 if sending data successfully, otherwise it is failed. + **/ +int vpu_ipi_send(struct platform_device *pdev, + enum ipi_id id, void *buf, + unsigned int len); + +/** + * vpu_get_plat_device - get VPU's platform device + * + * @pdev: the platform device of the module requesting VPU platform + * device for using VPU API. + * + * Return: Return NULL if it is failed. + * otherwise it is VPU's platform device + **/ +struct platform_device *vpu_get_plat_device(struct platform_device *pdev); + +/** + * vpu_wdt_reg_handler - register a VPU watchdog handler + * + * @pdev: VPU platform device + * @vpu_wdt_reset_func: the callback reset function + * @private_data: the private data for reset function + * @rst_id: reset id + * + * Register a handler performing own tasks when vpu reset by watchdog + * + * Return: Return 0 if the handler is added successfully, + * otherwise it is failed. + * + **/ +int vpu_wdt_reg_handler(struct platform_device *pdev, + void vpu_wdt_reset_func(void *), + void *priv, enum rst_id id); +/** + * vpu_get_venc_hw_capa - get video encoder hardware capability + * + * @pdev: VPU platform device + * + * Return: video encoder hardware capability + **/ +unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev); + +/** + * vpu_load_firmware - download VPU firmware and boot it + * + * @pdev: VPU platform device + * + * Return: Return 0 if downloading firmware successfully, + * otherwise it is failed + **/ +int vpu_load_firmware(struct platform_device *pdev); + +/** + * vpu_mapping_dm_addr - Mapping DTCM/DMEM to kernel virtual address + * + * @pdev: VPU platform device + * @dmem_addr: VPU's data memory address + * + * Mapping the VPU's DTCM (Data Tightly-Coupled Memory) / + * DMEM (Data Extended Memory) memory address to + * kernel virtual address. + * + * Return: Return ERR_PTR(-EINVAL) if mapping failed, + * otherwise the mapped kernel virtual address + **/ +void *vpu_mapping_dm_addr(struct platform_device *pdev, + u32 dtcm_dmem_addr); +#endif /* _MTK_VPU_H */ diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 3c4012d42d69..c639406fe72e 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -211,7 +211,6 @@ struct emmaprp_dev { struct clk *clk_emma_ahb, *clk_emma_ipg; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; }; struct emmaprp_ctx { @@ -690,7 +689,7 @@ static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { */ static int emmaprp_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq); struct emmaprp_q_data *q_data; @@ -710,8 +709,6 @@ static int emmaprp_queue_setup(struct vb2_queue *vq, *nbuffers = count; sizes[0] = size; - alloc_ctxs[0] = ctx->dev->alloc_ctx; - dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; @@ -765,6 +762,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &emmaprp_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = ctx->dev->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -777,6 +775,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &emmaprp_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = ctx->dev->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -948,18 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev) if (ret) goto rel_vdev; - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(pcdev->alloc_ctx); - goto rel_vdev; - } - pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(pcdev->m2m_dev)) { v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(pcdev->m2m_dev); - goto rel_ctx; + goto rel_vdev; } ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); @@ -973,8 +965,6 @@ static int emmaprp_probe(struct platform_device *pdev) rel_m2m: v4l2_m2m_release(pcdev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); rel_vdev: video_device_release(vfd); unreg_dev: @@ -993,7 +983,6 @@ static int emmaprp_remove(struct platform_device *pdev) video_unregister_device(pcdev->vfd); v4l2_m2m_release(pcdev->m2m_dev); - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); v4l2_device_unregister(&pcdev->v4l2_dev); mutex_destroy(&pcdev->dev_mutex); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 70c28d19ea04..4afc999c0780 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1318,71 +1318,16 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr return ret; } -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *ctrl) +static int omap_vout_s_ctrl(struct v4l2_ctrl *ctrl) { + struct omap_vout_device *vout = + container_of(ctrl->handler, struct omap_vout_device, ctrl_handler); int ret = 0; switch (ctrl->id) { - case V4L2_CID_ROTATE: - ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); - break; - case V4L2_CID_BG_COLOR: - ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0); - break; - case V4L2_CID_VFLIP: - ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); - break; - default: - ctrl->name[0] = '\0'; - ret = -EINVAL; - } - return ret; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) -{ - int ret = 0; - struct omap_vout_device *vout = fh; - - switch (ctrl->id) { - case V4L2_CID_ROTATE: - ctrl->value = vout->control[0].value; - break; - case V4L2_CID_BG_COLOR: - { - struct omap_overlay_manager_info info; - struct omap_overlay *ovl; - - ovl = vout->vid_info.overlays[0]; - if (!ovl->manager || !ovl->manager->get_manager_info) { - ret = -EINVAL; - break; - } - - ovl->manager->get_manager_info(ovl->manager, &info); - ctrl->value = info.default_color; - break; - } - case V4L2_CID_VFLIP: - ctrl->value = vout->control[2].value; - break; - default: - ret = -EINVAL; - } - return ret; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) -{ - int ret = 0; - struct omap_vout_device *vout = fh; - - switch (a->id) { - case V4L2_CID_ROTATE: - { + case V4L2_CID_ROTATE: { struct omapvideo_info *ovid; - int rotation = a->value; + int rotation = ctrl->val; ovid = &vout->vid_info; @@ -1405,15 +1350,13 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) ret = -EINVAL; break; } - - vout->control[0].value = rotation; mutex_unlock(&vout->lock); break; } case V4L2_CID_BG_COLOR: { struct omap_overlay *ovl; - unsigned int color = a->value; + unsigned int color = ctrl->val; struct omap_overlay_manager_info info; ovl = vout->vid_info.overlays[0]; @@ -1432,15 +1375,13 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) ret = -EINVAL; break; } - - vout->control[1].value = color; mutex_unlock(&vout->lock); break; } case V4L2_CID_VFLIP: { struct omapvideo_info *ovid; - unsigned int mirror = a->value; + unsigned int mirror = ctrl->val; ovid = &vout->vid_info; @@ -1457,16 +1398,19 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) break; } vout->mirror = mirror; - vout->control[2].value = mirror; mutex_unlock(&vout->lock); break; } default: - ret = -EINVAL; + return -EINVAL; } return ret; } +static const struct v4l2_ctrl_ops omap_vout_ctrl_ops = { + .s_ctrl = omap_vout_s_ctrl, +}; + static int vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) { @@ -1831,11 +1775,8 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, @@ -1865,9 +1806,9 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) { struct video_device *vfd; struct v4l2_pix_format *pix; - struct v4l2_control *control; struct omap_overlay *ovl = vout->vid_info.overlays[0]; struct omap_dss_device *display = ovl->get_device(ovl); + struct v4l2_ctrl_handler *hdl; /* set the default pix */ pix = &vout->pix; @@ -1896,29 +1837,32 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win); - /*Initialize the control variables for - rotation, flipping and background color. */ - control = vout->control; - control[0].id = V4L2_CID_ROTATE; - control[0].value = 0; + hdl = &vout->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 3); + v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); + v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops, + V4L2_CID_BG_COLOR, 0, 0xffffff, 1, 0); + v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (hdl->error) + return hdl->error; + vout->rotation = 0; vout->mirror = false; - vout->control[2].id = V4L2_CID_HFLIP; - vout->control[2].value = 0; if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) vout->vrfb_bpp = 2; - control[1].id = V4L2_CID_BG_COLOR; - control[1].value = 0; - /* initialize the video_device struct */ vfd = vout->vfd = video_device_alloc(); if (!vfd) { printk(KERN_ERR VOUT_NAME ": could not allocate" " video device struct\n"); + v4l2_ctrl_handler_free(hdl); return -ENOMEM; } + vfd->ctrl_handler = hdl; vfd->release = video_device_release; vfd->ioctl_ops = &vout_ioctl_ops; @@ -2092,6 +2036,7 @@ static void omap_vout_cleanup_device(struct omap_vout_device *vout) video_unregister_device(vfd); } } + v4l2_ctrl_handler_free(&vout->ctrl_handler); if (ovid->rotation_type == VOUT_ROT_VRFB) { omap_vout_release_vrfb(vout); /* Free the VRFB buffer if allocated diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/omap/omap_voutdef.h index 9ccfe1f475a4..49de1475e473 100644 --- a/drivers/media/platform/omap/omap_voutdef.h +++ b/drivers/media/platform/omap/omap_voutdef.h @@ -11,6 +11,7 @@ #ifndef OMAP_VOUTDEF_H #define OMAP_VOUTDEF_H +#include #include