diff options
Diffstat (limited to 'drivers/media')
393 files changed, 35713 insertions, 11380 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 145e12bfb819..37124c3b8c2a 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -141,15 +141,18 @@ config DVB_CORE tristate depends on MEDIA_SUPPORT depends on MEDIA_DIGITAL_TV_SUPPORT + depends on (I2C || I2C=n) default y select CRC32 config DVB_MMAP bool "Enable DVB memory-mapped API (EXPERIMENTAL)" depends on DVB_CORE + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_CORE + select VIDEOBUF2_VMALLOC default n help - This option enables DVB experimental memory-mapped API, with + This option enables DVB experimental memory-mapped API, which reduces the number of context switches to read DVB buffers, as the buffers can use mmap() syscalls. diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index 43428cec3a01..9c2b108c613a 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -4,3 +4,9 @@ config MEDIA_CEC_RC depends on CEC_CORE=m || RC_CORE=y ---help--- Pass on CEC remote control messages to the RC framework. + +config CEC_PIN_ERROR_INJ + bool "Enable CEC error injection support" + depends on CEC_PIN && DEBUG_FS + ---help--- + This option enables CEC error injection using debugfs. diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile index 41ee3325e1ea..29a2ab9e77c5 100644 --- a/drivers/media/cec/Makefile +++ b/drivers/media/cec/Makefile @@ -9,4 +9,8 @@ ifeq ($(CONFIG_CEC_PIN),y) cec-objs += cec-pin.o endif +ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y) + cec-objs += cec-pin-error-inj.o +endif + obj-$(CONFIG_CEC_CORE) += cec.o diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 2b1e540587d6..002ed4c90371 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter * * 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 <linux/errno.h> @@ -85,8 +73,8 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { - static const u8 max_events[CEC_NUM_EVENTS] = { - 1, 1, 64, 64, 8, 8, + static const u16 max_events[CEC_NUM_EVENTS] = { + 1, 1, 800, 800, 8, 8, }; struct cec_event_entry *entry; unsigned int ev_idx = new_ev->event - 1; @@ -154,11 +142,13 @@ static void cec_queue_event(struct cec_adapter *adap, } /* Notify userspace that the CEC pin changed state at the given time. */ -void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts) +void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, + bool dropped_events, ktime_t ts) { struct cec_event ev = { .event = is_high ? CEC_EVENT_PIN_CEC_HIGH : CEC_EVENT_PIN_CEC_LOW, + .flags = dropped_events ? CEC_EVENT_FL_DROPPED_EVENTS : 0, }; struct cec_fh *fh; @@ -711,16 +701,31 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, else msg->flags = 0; + if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) { + msg->msg[2] = adap->phys_addr >> 8; + msg->msg[3] = adap->phys_addr & 0xff; + } + /* Sanity checks */ if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) { dprintk(1, "%s: invalid length %d\n", __func__, msg->len); return -EINVAL; } + + memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len); + + if (msg->timeout) + dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n", + __func__, msg->len, msg->msg, msg->reply, + !block ? ", nb" : ""); + else + dprintk(2, "%s: %*ph%s\n", + __func__, msg->len, msg->msg, !block ? " (nb)" : ""); + if (msg->timeout && msg->len == 1) { - dprintk(1, "%s: can't reply for poll msg\n", __func__); + dprintk(1, "%s: can't reply to poll msg\n", __func__); return -EINVAL; } - memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len); if (msg->len == 1) { if (cec_msg_destination(msg) == 0xf) { dprintk(1, "%s: invalid poll message\n", __func__); @@ -780,19 +785,6 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, if (!msg->sequence) msg->sequence = ++adap->sequence; - if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) { - msg->msg[2] = adap->phys_addr >> 8; - msg->msg[3] = adap->phys_addr & 0xff; - } - - if (msg->timeout) - dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n", - __func__, msg->len, msg->msg, msg->reply, - !block ? ", nb" : ""); - else - dprintk(2, "%s: %*ph%s\n", - __func__, msg->len, msg->msg, !block ? " (nb)" : ""); - data->msg = *msg; data->fh = fh; data->adap = adap; diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 492db12b8c4d..10b67fc40318 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cec-api.c - HDMI Consumer Electronics Control framework - API * * 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 <linux/errno.h> diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index a9f9525db9ae..b0c87f9ea08f 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cec-core.c - HDMI Consumer Electronics Control framework - Core * * 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 <linux/errno.h> @@ -207,6 +195,55 @@ void cec_register_cec_notifier(struct cec_adapter *adap, EXPORT_SYMBOL_GPL(cec_register_cec_notifier); #endif +#ifdef CONFIG_DEBUG_FS +static ssize_t cec_error_inj_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct cec_adapter *adap = sf->private; + char *buf; + char *line; + char *p; + + buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count)); + if (IS_ERR(buf)) + return PTR_ERR(buf); + p = buf; + while (p && *p) { + p = skip_spaces(p); + line = strsep(&p, "\n"); + if (!*line || *line == '#') + continue; + if (!adap->ops->error_inj_parse_line(adap, line)) { + kfree(buf); + return -EINVAL; + } + } + kfree(buf); + return count; +} + +static int cec_error_inj_show(struct seq_file *sf, void *unused) +{ + struct cec_adapter *adap = sf->private; + + return adap->ops->error_inj_show(adap, sf); +} + +static int cec_error_inj_open(struct inode *inode, struct file *file) +{ + return single_open(file, cec_error_inj_show, inode->i_private); +} + +static const struct file_operations cec_error_inj_fops = { + .open = cec_error_inj_open, + .write = cec_error_inj_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las) @@ -346,7 +383,16 @@ int cec_register_adapter(struct cec_adapter *adap, pr_warn("cec-%s: Failed to create status file\n", adap->name); debugfs_remove_recursive(adap->cec_dir); adap->cec_dir = NULL; + return 0; } + if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line) + return 0; + adap->error_inj_file = debugfs_create_file("error-inj", 0644, + adap->cec_dir, adap, + &cec_error_inj_fops); + if (IS_ERR_OR_NULL(adap->error_inj_file)) + pr_warn("cec-%s: Failed to create error-inj file\n", + adap->name); #endif return 0; } diff --git a/drivers/media/cec/cec-edid.c b/drivers/media/cec/cec-edid.c index 38e3fec6152b..ec72ac1c0b91 100644 --- a/drivers/media/cec/cec-edid.c +++ b/drivers/media/cec/cec-edid.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 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 <linux/module.h> diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c index 08b619d0ea1e..16dffa06c913 100644 --- a/drivers/media/cec/cec-notifier.c +++ b/drivers/media/cec/cec-notifier.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cec-notifier.c - notify CEC drivers of physical address changes * * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> * Copyright 2016-2017 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 <linux/export.h> diff --git a/drivers/media/cec/cec-pin-error-inj.c b/drivers/media/cec/cec-pin-error-inj.c new file mode 100644 index 000000000000..aaa899a175ce --- /dev/null +++ b/drivers/media/cec/cec-pin-error-inj.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/sched/types.h> + +#include <media/cec-pin.h> +#include "cec-pin-priv.h" + +struct cec_error_inj_cmd { + unsigned int mode_offset; + int arg_idx; + const char *cmd; +}; + +static const struct cec_error_inj_cmd cec_error_inj_cmds[] = { + { CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" }, + { CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET, + CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" }, + { CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" }, + { CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" }, + { CEC_ERROR_INJ_RX_ARB_LOST_OFFSET, + CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" }, + + { CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" }, + { CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" }, + { CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET, + CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" }, + { CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" }, + { CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET, + CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" }, + { CEC_ERROR_INJ_TX_LONG_BIT_OFFSET, + CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" }, + { CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET, + CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" }, + { CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" }, + { CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" }, + { CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" }, + { CEC_ERROR_INJ_TX_LAST_BIT_OFFSET, + CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" }, + { CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET, + CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" }, + { 0, -1, NULL } +}; + +u16 cec_pin_rx_error_inj(struct cec_pin *pin) +{ + u16 cmd = CEC_ERROR_INJ_OP_ANY; + + /* Only when 18 bits have been received do we have a valid cmd */ + if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) && + pin->rx_bit >= 18) + cmd = pin->rx_msg.msg[1]; + return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd : + CEC_ERROR_INJ_OP_ANY; +} + +u16 cec_pin_tx_error_inj(struct cec_pin *pin) +{ + u16 cmd = CEC_ERROR_INJ_OP_ANY; + + if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) && + pin->tx_msg.len > 1) + cmd = pin->tx_msg.msg[1]; + return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd : + CEC_ERROR_INJ_OP_ANY; +} + +bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) +{ + static const char *delims = " \t\r"; + struct cec_pin *pin = adap->pin; + unsigned int i; + bool has_pos = false; + char *p = line; + char *token; + char *comma; + u64 *error; + u8 *args; + bool has_op; + u32 op; + u8 mode; + u8 pos; + u8 v; + + p = skip_spaces(p); + token = strsep(&p, delims); + if (!strcmp(token, "clear")) { + memset(pin->error_inj, 0, sizeof(pin->error_inj)); + pin->rx_toggle = pin->tx_toggle = false; + pin->tx_ignore_nack_until_eom = false; + pin->tx_custom_pulse = false; + pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT; + pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT; + return true; + } + if (!strcmp(token, "rx-clear")) { + for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++) + pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK; + pin->rx_toggle = false; + return true; + } + if (!strcmp(token, "tx-clear")) { + for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++) + pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK; + pin->tx_toggle = false; + pin->tx_ignore_nack_until_eom = false; + pin->tx_custom_pulse = false; + pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT; + pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT; + return true; + } + if (!strcmp(token, "tx-ignore-nack-until-eom")) { + pin->tx_ignore_nack_until_eom = true; + return true; + } + if (!strcmp(token, "tx-custom-pulse")) { + pin->tx_custom_pulse = true; + cec_pin_start_timer(pin); + return true; + } + if (!p) + return false; + + p = skip_spaces(p); + if (!strcmp(token, "tx-custom-low-usecs")) { + u32 usecs; + + if (kstrtou32(p, 0, &usecs) || usecs > 10000000) + return false; + pin->tx_custom_low_usecs = usecs; + return true; + } + if (!strcmp(token, "tx-custom-high-usecs")) { + u32 usecs; + + if (kstrtou32(p, 0, &usecs) || usecs > 10000000) + return false; + pin->tx_custom_high_usecs = usecs; + return true; + } + + comma = strchr(token, ','); + if (comma) + *comma++ = '\0'; + if (!strcmp(token, "any")) + op = CEC_ERROR_INJ_OP_ANY; + else if (!kstrtou8(token, 0, &v)) + op = v; + else + return false; + mode = CEC_ERROR_INJ_MODE_ONCE; + if (comma) { + if (!strcmp(comma, "off")) + mode = CEC_ERROR_INJ_MODE_OFF; + else if (!strcmp(comma, "once")) + mode = CEC_ERROR_INJ_MODE_ONCE; + else if (!strcmp(comma, "always")) + mode = CEC_ERROR_INJ_MODE_ALWAYS; + else if (!strcmp(comma, "toggle")) + mode = CEC_ERROR_INJ_MODE_TOGGLE; + else + return false; + } + + error = pin->error_inj + op; + args = pin->error_inj_args[op]; + has_op = op <= 0xff; + + token = strsep(&p, delims); + if (p) { + p = skip_spaces(p); + has_pos = !kstrtou8(p, 0, &pos); + } + + if (!strcmp(token, "clear")) { + *error = 0; + return true; + } + if (!strcmp(token, "rx-clear")) { + *error &= ~CEC_ERROR_INJ_RX_MASK; + return true; + } + if (!strcmp(token, "tx-clear")) { + *error &= ~CEC_ERROR_INJ_TX_MASK; + return true; + } + + for (i = 0; cec_error_inj_cmds[i].cmd; i++) { + const char *cmd = cec_error_inj_cmds[i].cmd; + unsigned int mode_offset; + u64 mode_mask; + int arg_idx; + bool is_bit_pos = true; + + if (strcmp(token, cmd)) + continue; + + mode_offset = cec_error_inj_cmds[i].mode_offset; + mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset; + arg_idx = cec_error_inj_cmds[i].arg_idx; + + if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET || + mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) + is_bit_pos = false; + + if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) { + if (has_op) + return false; + if (!has_pos) + pos = 0x0f; + } + if (arg_idx >= 0 && is_bit_pos) { + if (!has_pos || pos >= 160) + return false; + if (has_op && pos < 10 + 8) + return false; + /* Invalid bit position may not be the Ack bit */ + if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET || + mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET || + mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) && + (pos % 10) == 9) + return false; + } + *error &= ~mode_mask; + *error |= (u64)mode << mode_offset; + if (arg_idx >= 0) + args[arg_idx] = pos; + return true; + } + return false; +} + +static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode) +{ + if (cmd == CEC_ERROR_INJ_OP_ANY) + seq_puts(sf, "any,"); + else + seq_printf(sf, "0x%02x,", cmd); + switch (mode) { + case CEC_ERROR_INJ_MODE_ONCE: + seq_puts(sf, "once "); + break; + case CEC_ERROR_INJ_MODE_ALWAYS: + seq_puts(sf, "always "); + break; + case CEC_ERROR_INJ_MODE_TOGGLE: + seq_puts(sf, "toggle "); + break; + default: + seq_puts(sf, "off "); + break; + } +} + +int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) +{ + struct cec_pin *pin = adap->pin; + unsigned int i, j; + + seq_puts(sf, "# Clear error injections:\n"); + seq_puts(sf, "# clear clear all rx and tx error injections\n"); + seq_puts(sf, "# rx-clear clear all rx error injections\n"); + seq_puts(sf, "# tx-clear clear all tx error injections\n"); + seq_puts(sf, "# <op> clear clear all rx and tx error injections for <op>\n"); + seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n"); + seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n"); + seq_puts(sf, "#\n"); + seq_puts(sf, "# RX error injection:\n"); + seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n"); + seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n"); + seq_puts(sf, "# <op>[,<mode>] rx-add-byte add a spurious byte to the received CEC message\n"); + seq_puts(sf, "# <op>[,<mode>] rx-remove-byte remove the last byte from the received CEC message\n"); + seq_puts(sf, "# <op>[,<mode>] rx-arb-lost <poll> generate a POLL message to trigger an arbitration lost\n"); + seq_puts(sf, "#\n"); + seq_puts(sf, "# TX error injection settings:\n"); + seq_puts(sf, "# tx-ignore-nack-until-eom ignore early NACKs until EOM\n"); + seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n"); + seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n"); + seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n"); + seq_puts(sf, "#\n"); + seq_puts(sf, "# TX error injection:\n"); + seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n"); + seq_puts(sf, "# <op>[,<mode>] tx-early-eom set the EOM bit one byte too soon\n"); + seq_puts(sf, "# <op>[,<mode>] tx-add-bytes <num> append <num> (1-255) spurious bytes to the message\n"); + seq_puts(sf, "# <op>[,<mode>] tx-remove-byte drop the last byte from the message\n"); + seq_puts(sf, "# <op>[,<mode>] tx-short-bit <bit> make this bit shorter than allowed\n"); + seq_puts(sf, "# <op>[,<mode>] tx-long-bit <bit> make this bit longer than allowed\n"); + seq_puts(sf, "# <op>[,<mode>] tx-custom-bit <bit> send the custom pulse instead of this bit\n"); + seq_puts(sf, "# <op>[,<mode>] tx-short-start send a start pulse that's too short\n"); + seq_puts(sf, "# <op>[,<mode>] tx-long-start send a start pulse that's too long\n"); + seq_puts(sf, "# <op>[,<mode>] tx-custom-start send the custom pulse instead of the start pulse\n"); + seq_puts(sf, "# <op>[,<mode>] tx-last-bit <bit> stop sending after this bit\n"); + seq_puts(sf, "# <op>[,<mode>] tx-low-drive <bit> force a low-drive condition at this bit position\n"); + seq_puts(sf, "#\n"); + seq_puts(sf, "# <op> CEC message opcode (0-255) or 'any'\n"); + seq_puts(sf, "# <mode> 'once' (default), 'always', 'toggle' or 'off'\n"); + seq_puts(sf, "# <bit> CEC message bit (0-159)\n"); + seq_puts(sf, "# 10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n"); + seq_puts(sf, "# <poll> CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n"); + seq_puts(sf, "# <usecs> microseconds (0-10000000, default 1000)\n"); + + seq_puts(sf, "\nclear\n"); + + for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) { + u64 e = pin->error_inj[i]; + + for (j = 0; cec_error_inj_cmds[j].cmd; j++) { + const char *cmd = cec_error_inj_cmds[j].cmd; + unsigned int mode; + unsigned int mode_offset; + int arg_idx; + + mode_offset = cec_error_inj_cmds[j].mode_offset; + arg_idx = cec_error_inj_cmds[j].arg_idx; + mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK; + if (!mode) + continue; + cec_pin_show_cmd(sf, i, mode); + seq_puts(sf, cmd); + if (arg_idx >= 0) + seq_printf(sf, " %u", + pin->error_inj_args[i][arg_idx]); + seq_puts(sf, "\n"); + } + } + + if (pin->tx_ignore_nack_until_eom) + seq_puts(sf, "tx-ignore-nack-until-eom\n"); + if (pin->tx_custom_pulse) + seq_puts(sf, "tx-custom-pulse\n"); + if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT) + seq_printf(sf, "tx-custom-low-usecs %u\n", + pin->tx_custom_low_usecs); + if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT) + seq_printf(sf, "tx-custom-high-usecs %u\n", + pin->tx_custom_high_usecs); + return 0; +} diff --git a/drivers/media/cec/cec-pin-priv.h b/drivers/media/cec/cec-pin-priv.h index 7d0def199762..f423db8855d9 100644 --- a/drivers/media/cec/cec-pin-priv.h +++ b/drivers/media/cec/cec-pin-priv.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cec-pin-priv.h - internal cec-pin header * * Copyright 2017 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. */ #ifndef LINUX_CEC_PIN_PRIV_H @@ -40,14 +28,30 @@ enum cec_pin_state { CEC_ST_TX_START_BIT_LOW, /* Drive CEC high for the start bit */ CEC_ST_TX_START_BIT_HIGH, + /* Generate a start bit period that is too short */ + CEC_ST_TX_START_BIT_HIGH_SHORT, + /* Generate a start bit period that is too long */ + CEC_ST_TX_START_BIT_HIGH_LONG, + /* Drive CEC low for the start bit using the custom timing */ + CEC_ST_TX_START_BIT_LOW_CUSTOM, + /* Drive CEC high for the start bit using the custom timing */ + CEC_ST_TX_START_BIT_HIGH_CUSTOM, /* Drive CEC low for the 0 bit */ CEC_ST_TX_DATA_BIT_0_LOW, /* Drive CEC high for the 0 bit */ CEC_ST_TX_DATA_BIT_0_HIGH, + /* Generate a bit period that is too short */ + CEC_ST_TX_DATA_BIT_0_HIGH_SHORT, + /* Generate a bit period that is too long */ + CEC_ST_TX_DATA_BIT_0_HIGH_LONG, /* Drive CEC low for the 1 bit */ CEC_ST_TX_DATA_BIT_1_LOW, /* Drive CEC high for the 1 bit */ CEC_ST_TX_DATA_BIT_1_HIGH, + /* Generate a bit period that is too short */ + CEC_ST_TX_DATA_BIT_1_HIGH_SHORT, + /* Generate a bit period that is too long */ + CEC_ST_TX_DATA_BIT_1_HIGH_LONG, /* * Wait for start of sample time to check for Ack bit or first * four initiator bits to check for Arbitration Lost. @@ -55,6 +59,20 @@ enum cec_pin_state { CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE, /* Wait for end of bit period after sampling */ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE, + /* Generate a bit period that is too short */ + CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT, + /* Generate a bit period that is too long */ + CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG, + /* Drive CEC low for a data bit using the custom timing */ + CEC_ST_TX_DATA_BIT_LOW_CUSTOM, + /* Drive CEC high for a data bit using the custom timing */ + CEC_ST_TX_DATA_BIT_HIGH_CUSTOM, + /* Drive CEC low for a standalone pulse using the custom timing */ + CEC_ST_TX_PULSE_LOW_CUSTOM, + /* Drive CEC high for a standalone pulse using the custom timing */ + CEC_ST_TX_PULSE_HIGH_CUSTOM, + /* Start low drive */ + CEC_ST_TX_LOW_DRIVE, /* Rx states */ @@ -66,8 +84,8 @@ enum cec_pin_state { CEC_ST_RX_DATA_SAMPLE, /* Wait for earliest end of bit period after sampling */ CEC_ST_RX_DATA_POST_SAMPLE, - /* Wait for CEC to go high (i.e. end of bit period */ - CEC_ST_RX_DATA_HIGH, + /* Wait for CEC to go low (i.e. end of bit period) */ + CEC_ST_RX_DATA_WAIT_FOR_LOW, /* Drive CEC low to send 0 Ack bit */ CEC_ST_RX_ACK_LOW, /* End of 0 Ack time, wait for earliest end of bit period */ @@ -76,9 +94,9 @@ enum cec_pin_state { CEC_ST_RX_ACK_HIGH_POST, /* Wait for earliest end of bit period and end of message */ CEC_ST_RX_ACK_FINISH, - /* Start low drive */ - CEC_ST_LOW_DRIVE, + CEC_ST_RX_LOW_DRIVE, + /* Monitor pin using interrupts */ CEC_ST_RX_IRQ, @@ -86,7 +104,58 @@ enum cec_pin_state { CEC_PIN_STATES }; -#define CEC_NUM_PIN_EVENTS 128 +/* Error Injection */ + +/* Error injection modes */ +#define CEC_ERROR_INJ_MODE_OFF 0 +#define CEC_ERROR_INJ_MODE_ONCE 1 +#define CEC_ERROR_INJ_MODE_ALWAYS 2 +#define CEC_ERROR_INJ_MODE_TOGGLE 3 +#define CEC_ERROR_INJ_MODE_MASK 3ULL + +/* Receive error injection options */ +#define CEC_ERROR_INJ_RX_NACK_OFFSET 0 +#define CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET 2 +#define CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET 4 +#define CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET 6 +#define CEC_ERROR_INJ_RX_ARB_LOST_OFFSET 8 +#define CEC_ERROR_INJ_RX_MASK 0xffffULL + +/* Transmit error injection options */ +#define CEC_ERROR_INJ_TX_NO_EOM_OFFSET 16 +#define CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET 18 +#define CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET 20 +#define CEC_ERROR_INJ_TX_LONG_BIT_OFFSET 22 +#define CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET 24 +#define CEC_ERROR_INJ_TX_SHORT_START_OFFSET 26 +#define CEC_ERROR_INJ_TX_LONG_START_OFFSET 28 +#define CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET 30 +#define CEC_ERROR_INJ_TX_LAST_BIT_OFFSET 32 +#define CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET 34 +#define CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET 36 +#define CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET 38 +#define CEC_ERROR_INJ_TX_MASK 0xffffffffffff0000ULL + +#define CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX 0 +#define CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX 1 + +#define CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX 2 +#define CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX 3 +#define CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX 4 +#define CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX 5 +#define CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX 6 +#define CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX 7 +#define CEC_ERROR_INJ_NUM_ARGS 8 + +/* Special CEC op values */ +#define CEC_ERROR_INJ_OP_ANY 0x00000100 + +/* The default for the low/high time of the custom pulse */ +#define CEC_TIM_CUSTOM_DEFAULT 1000 + +#define CEC_NUM_PIN_EVENTS 128 +#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0) +#define CEC_PIN_EVENT_FL_DROPPED (1 << 1) #define CEC_PIN_IRQ_UNCHANGED 0 #define CEC_PIN_IRQ_DISABLE 1 @@ -110,24 +179,63 @@ struct cec_pin { u32 tx_bit; bool tx_nacked; u32 tx_signal_free_time; + bool tx_toggle; struct cec_msg rx_msg; u32 rx_bit; + bool rx_toggle; + u32 rx_start_bit_low_too_short_cnt; + u64 rx_start_bit_low_too_short_ts; + u32 rx_start_bit_low_too_short_delta; + u32 rx_start_bit_too_short_cnt; + u64 rx_start_bit_too_short_ts; + u32 rx_start_bit_too_short_delta; + u32 rx_start_bit_too_long_cnt; + u32 rx_data_bit_too_short_cnt; + u64 rx_data_bit_too_short_ts; + u32 rx_data_bit_too_short_delta; + u32 rx_data_bit_too_long_cnt; + u32 rx_low_drive_cnt; struct cec_msg work_rx_msg; u8 work_tx_status; ktime_t work_tx_ts; atomic_t work_irq_change; - atomic_t work_pin_events; + atomic_t work_pin_num_events; unsigned int work_pin_events_wr; unsigned int work_pin_events_rd; ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS]; - bool work_pin_is_high[CEC_NUM_PIN_EVENTS]; + u8 work_pin_events[CEC_NUM_PIN_EVENTS]; + bool work_pin_events_dropped; + u32 work_pin_events_dropped_cnt; ktime_t timer_ts; u32 timer_cnt; u32 timer_100ms_overruns; u32 timer_300ms_overruns; u32 timer_max_overrun; u32 timer_sum_overrun; + + u32 tx_custom_low_usecs; + u32 tx_custom_high_usecs; + bool tx_ignore_nack_until_eom; + bool tx_custom_pulse; + bool tx_generated_poll; + bool tx_post_eom; + u8 tx_extra_bytes; + u32 tx_low_drive_cnt; +#ifdef CONFIG_CEC_PIN_ERROR_INJ + u64 error_inj[CEC_ERROR_INJ_OP_ANY + 1]; + u8 error_inj_args[CEC_ERROR_INJ_OP_ANY + 1][CEC_ERROR_INJ_NUM_ARGS]; +#endif }; +void cec_pin_start_timer(struct cec_pin *pin); + +#ifdef CONFIG_CEC_PIN_ERROR_INJ +bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line); +int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf); + +u16 cec_pin_rx_error_inj(struct cec_pin *pin); +u16 cec_pin_tx_error_inj(struct cec_pin *pin); +#endif + #endif diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c index b48dfe844118..fafe1ebc8aff 100644 --- a/drivers/media/cec/cec-pin.c +++ b/drivers/media/cec/cec-pin.c @@ -1,18 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2017 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 <linux/delay.h> @@ -51,11 +39,29 @@ #define CEC_TIM_IDLE_SAMPLE 1000 /* when processing the start bit, sample twice per millisecond */ #define CEC_TIM_START_BIT_SAMPLE 500 -/* when polling for a state change, sample once every 50 micoseconds */ +/* when polling for a state change, sample once every 50 microseconds */ #define CEC_TIM_SAMPLE 50 #define CEC_TIM_LOW_DRIVE_ERROR (1.5 * CEC_TIM_DATA_BIT_TOTAL) +/* + * Total data bit time that is too short/long for a valid bit, + * used for error injection. + */ +#define CEC_TIM_DATA_BIT_TOTAL_SHORT 1800 +#define CEC_TIM_DATA_BIT_TOTAL_LONG 2900 + +/* + * Total start bit time that is too short/long for a valid bit, + * used for error injection. + */ +#define CEC_TIM_START_BIT_TOTAL_SHORT 4100 +#define CEC_TIM_START_BIT_TOTAL_LONG 5000 + +/* Data bits are 0-7, EOM is bit 8 and ACK is bit 9 */ +#define EOM_BIT 8 +#define ACK_BIT 9 + struct cec_state { const char * const name; unsigned int usecs; @@ -68,17 +74,32 @@ static const struct cec_state states[CEC_PIN_STATES] = { { "Tx Wait for High", CEC_TIM_IDLE_SAMPLE }, { "Tx Start Bit Low", CEC_TIM_START_BIT_LOW }, { "Tx Start Bit High", CEC_TIM_START_BIT_TOTAL - CEC_TIM_START_BIT_LOW }, + { "Tx Start Bit High Short", CEC_TIM_START_BIT_TOTAL_SHORT - CEC_TIM_START_BIT_LOW }, + { "Tx Start Bit High Long", CEC_TIM_START_BIT_TOTAL_LONG - CEC_TIM_START_BIT_LOW }, + { "Tx Start Bit Low Custom", 0 }, + { "Tx Start Bit High Custom", 0 }, { "Tx Data 0 Low", CEC_TIM_DATA_BIT_0_LOW }, { "Tx Data 0 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_0_LOW }, + { "Tx Data 0 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_0_LOW }, + { "Tx Data 0 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_0_LOW }, { "Tx Data 1 Low", CEC_TIM_DATA_BIT_1_LOW }, { "Tx Data 1 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_1_LOW }, - { "Tx Data 1 Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW }, - { "Tx Data 1 Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE }, + { "Tx Data 1 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_1_LOW }, + { "Tx Data 1 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_1_LOW }, + { "Tx Data 1 High Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW }, + { "Tx Data 1 High Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE }, + { "Tx Data 1 High Post Sample Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_SAMPLE }, + { "Tx Data 1 High Post Sample Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_SAMPLE }, + { "Tx Data Bit Low Custom", 0 }, + { "Tx Data Bit High Custom", 0 }, + { "Tx Pulse Low Custom", 0 }, + { "Tx Pulse High Custom", 0 }, + { "Tx Low Drive", CEC_TIM_LOW_DRIVE_ERROR }, { "Rx Start Bit Low", CEC_TIM_SAMPLE }, { "Rx Start Bit High", CEC_TIM_SAMPLE }, { "Rx Data Sample", CEC_TIM_DATA_BIT_SAMPLE }, { "Rx Data Post Sample", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_SAMPLE }, - { "Rx Data High", CEC_TIM_SAMPLE }, + { "Rx Data Wait for Low", CEC_TIM_SAMPLE }, { "Rx Ack Low", CEC_TIM_DATA_BIT_0_LOW }, { "Rx Ack Low Post", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_0_LOW }, { "Rx Ack High Post", CEC_TIM_DATA_BIT_HIGH }, @@ -93,12 +114,21 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force) return; pin->adap->cec_pin_is_high = v; - if (atomic_read(&pin->work_pin_events) < CEC_NUM_PIN_EVENTS) { - pin->work_pin_is_high[pin->work_pin_events_wr] = v; + if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) { + u8 ev = v; + + if (pin->work_pin_events_dropped) { + pin->work_pin_events_dropped = false; + v |= CEC_PIN_EVENT_FL_DROPPED; + } + pin->work_pin_events[pin->work_pin_events_wr] = ev; pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get(); pin->work_pin_events_wr = (pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS; - atomic_inc(&pin->work_pin_events); + atomic_inc(&pin->work_pin_num_events); + } else { + pin->work_pin_events_dropped = true; + pin->work_pin_events_dropped_cnt++; } wake_up_interruptible(&pin->kthread_waitq); } @@ -123,6 +153,173 @@ static bool cec_pin_high(struct cec_pin *pin) return cec_pin_read(pin); } +static bool rx_error_inj(struct cec_pin *pin, unsigned int mode_offset, + int arg_idx, u8 *arg) +{ +#ifdef CONFIG_CEC_PIN_ERROR_INJ + u16 cmd = cec_pin_rx_error_inj(pin); + u64 e = pin->error_inj[cmd]; + unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK; + + if (arg_idx >= 0) { + u8 pos = pin->error_inj_args[cmd][arg_idx]; + + if (arg) + *arg = pos; + else if (pos != pin->rx_bit) + return false; + } + + switch (mode) { + case CEC_ERROR_INJ_MODE_ONCE: + pin->error_inj[cmd] &= + ~(CEC_ERROR_INJ_MODE_MASK << mode_offset); + return true; + case CEC_ERROR_INJ_MODE_ALWAYS: + return true; + case CEC_ERROR_INJ_MODE_TOGGLE: + return pin->rx_toggle; + default: + return false; + } +#else + return false; +#endif +} + +static bool rx_nack(struct cec_pin *pin) +{ + return rx_error_inj(pin, CEC_ERROR_INJ_RX_NACK_OFFSET, -1, NULL); +} + +static bool rx_low_drive(struct cec_pin *pin) +{ + return rx_error_inj(pin, CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET, + CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, NULL); +} + +static bool rx_add_byte(struct cec_pin *pin) +{ + return rx_error_inj(pin, CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, NULL); +} + +static bool rx_remove_byte(struct cec_pin *pin) +{ + return rx_error_inj(pin, CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, NULL); +} + +static bool rx_arb_lost(struct cec_pin *pin, u8 *poll) +{ + return pin->tx_msg.len == 0 && + rx_error_inj(pin, CEC_ERROR_INJ_RX_ARB_LOST_OFFSET, + CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, poll); +} + +static bool tx_error_inj(struct cec_pin *pin, unsigned int mode_offset, + int arg_idx, u8 *arg) +{ +#ifdef CONFIG_CEC_PIN_ERROR_INJ + u16 cmd = cec_pin_tx_error_inj(pin); + u64 e = pin->error_inj[cmd]; + unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK; + + if (arg_idx >= 0) { + u8 pos = pin->error_inj_args[cmd][arg_idx]; + + if (arg) + *arg = pos; + else if (pos != pin->tx_bit) + return false; + } + + switch (mode) { + case CEC_ERROR_INJ_MODE_ONCE: + pin->error_inj[cmd] &= + ~(CEC_ERROR_INJ_MODE_MASK << mode_offset); + return true; + case CEC_ERROR_INJ_MODE_ALWAYS: + return true; + case CEC_ERROR_INJ_MODE_TOGGLE: + return pin->tx_toggle; + default: + return false; + } +#else + return false; +#endif +} + +static bool tx_no_eom(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, NULL); +} + +static bool tx_early_eom(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, NULL); +} + +static bool tx_short_bit(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET, + CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, NULL); +} + +static bool tx_long_bit(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_BIT_OFFSET, + CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, NULL); +} + +static bool tx_custom_bit(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET, + CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, NULL); +} + +static bool tx_short_start(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, NULL); +} + +static bool tx_long_start(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, NULL); +} + +static bool tx_custom_start(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, + -1, NULL); +} + +static bool tx_last_bit(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_LAST_BIT_OFFSET, + CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, NULL); +} + +static u8 tx_add_bytes(struct cec_pin *pin) +{ + u8 bytes; + + if (tx_error_inj(pin, CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET, + CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, &bytes)) + return bytes; + return 0; +} + +static bool tx_remove_byte(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, NULL); +} + +static bool tx_low_drive(struct cec_pin *pin) +{ + return tx_error_inj(pin, CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET, + CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, NULL); +} + static void cec_pin_to_idle(struct cec_pin *pin) { /* @@ -132,8 +329,16 @@ static void cec_pin_to_idle(struct cec_pin *pin) pin->rx_bit = pin->tx_bit = 0; pin->rx_msg.len = 0; memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg)); - pin->state = CEC_ST_IDLE; pin->ts = ns_to_ktime(0); + pin->tx_generated_poll = false; + pin->tx_post_eom = false; + if (pin->state >= CEC_ST_TX_WAIT && + pin->state <= CEC_ST_TX_LOW_DRIVE) + pin->tx_toggle ^= 1; + if (pin->state >= CEC_ST_RX_START_BIT_LOW && + pin->state <= CEC_ST_RX_LOW_DRIVE) + pin->rx_toggle ^= 1; + pin->state = CEC_ST_IDLE; } /* @@ -174,42 +379,109 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) break; case CEC_ST_TX_START_BIT_LOW: - pin->state = CEC_ST_TX_START_BIT_HIGH; + if (tx_short_start(pin)) { + /* + * Error Injection: send an invalid (too short) + * start pulse. + */ + pin->state = CEC_ST_TX_START_BIT_HIGH_SHORT; + } else if (tx_long_start(pin)) { + /* + * Error Injection: send an invalid (too long) + * start pulse. + */ + pin->state = CEC_ST_TX_START_BIT_HIGH_LONG; + } else { + pin->state = CEC_ST_TX_START_BIT_HIGH; + } + /* Generate start bit */ + cec_pin_high(pin); + break; + + case CEC_ST_TX_START_BIT_LOW_CUSTOM: + pin->state = CEC_ST_TX_START_BIT_HIGH_CUSTOM; /* Generate start bit */ cec_pin_high(pin); break; case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE: - /* If the read value is 1, then all is OK */ - if (!cec_pin_read(pin)) { + case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT: + case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG: + if (pin->tx_nacked) { + cec_pin_to_idle(pin); + pin->tx_msg.len = 0; + if (pin->tx_generated_poll) + break; + pin->work_tx_ts = ts; + pin->work_tx_status = CEC_TX_STATUS_NACK; + wake_up_interruptible(&pin->kthread_waitq); + break; + } + /* fall through */ + case CEC_ST_TX_DATA_BIT_0_HIGH: + case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT: + case CEC_ST_TX_DATA_BIT_0_HIGH_LONG: + case CEC_ST_TX_DATA_BIT_1_HIGH: + case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT: + case CEC_ST_TX_DATA_BIT_1_HIGH_LONG: + /* + * If the read value is 1, then all is OK, otherwise we have a + * low drive condition. + * + * Special case: when we generate a poll message due to an + * Arbitration Lost error injection, then ignore this since + * the pin can actually be low in that case. + */ + if (!cec_pin_read(pin) && !pin->tx_generated_poll) { /* * It's 0, so someone detected an error and pulled the * line low for 1.5 times the nominal bit period. */ pin->tx_msg.len = 0; + pin->state = CEC_ST_TX_WAIT_FOR_HIGH; pin->work_tx_ts = ts; pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE; - pin->state = CEC_ST_TX_WAIT_FOR_HIGH; + pin->tx_low_drive_cnt++; wake_up_interruptible(&pin->kthread_waitq); break; } - if (pin->tx_nacked) { + /* fall through */ + case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM: + if (tx_last_bit(pin)) { + /* Error Injection: just stop sending after this bit */ cec_pin_to_idle(pin); pin->tx_msg.len = 0; + if (pin->tx_generated_poll) + break; pin->work_tx_ts = ts; - pin->work_tx_status = CEC_TX_STATUS_NACK; + pin->work_tx_status = CEC_TX_STATUS_OK; wake_up_interruptible(&pin->kthread_waitq); break; } - /* fall through */ - case CEC_ST_TX_DATA_BIT_0_HIGH: - case CEC_ST_TX_DATA_BIT_1_HIGH: pin->tx_bit++; /* fall through */ case CEC_ST_TX_START_BIT_HIGH: - if (pin->tx_bit / 10 >= pin->tx_msg.len) { + case CEC_ST_TX_START_BIT_HIGH_SHORT: + case CEC_ST_TX_START_BIT_HIGH_LONG: + case CEC_ST_TX_START_BIT_HIGH_CUSTOM: + if (tx_low_drive(pin)) { + /* Error injection: go to low drive */ + cec_pin_low(pin); + pin->state = CEC_ST_TX_LOW_DRIVE; + pin->tx_msg.len = 0; + if (pin->tx_generated_poll) + break; + pin->work_tx_ts = ts; + pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE; + pin->tx_low_drive_cnt++; + wake_up_interruptible(&pin->kthread_waitq); + break; + } + if (pin->tx_bit / 10 >= pin->tx_msg.len + pin->tx_extra_bytes) { cec_pin_to_idle(pin); pin->tx_msg.len = 0; + if (pin->tx_generated_poll) + break; pin->work_tx_ts = ts; pin->work_tx_status = CEC_TX_STATUS_OK; wake_up_interruptible(&pin->kthread_waitq); @@ -217,39 +489,82 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) } switch (pin->tx_bit % 10) { - default: - v = pin->tx_msg.msg[pin->tx_bit / 10] & - (1 << (7 - (pin->tx_bit % 10))); + default: { + /* + * In the CEC_ERROR_INJ_TX_ADD_BYTES case we transmit + * extra bytes, so pin->tx_bit / 10 can become >= 16. + * Generate bit values for those extra bytes instead + * of reading them from the transmit buffer. + */ + unsigned int idx = (pin->tx_bit / 10); + u8 val = idx; + + if (idx < pin->tx_msg.len) + val = pin->tx_msg.msg[idx]; + v = val & (1 << (7 - (pin->tx_bit % 10))); + pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW : - CEC_ST_TX_DATA_BIT_0_LOW; + CEC_ST_TX_DATA_BIT_0_LOW; break; - case 8: - v = pin->tx_bit / 10 == pin->tx_msg.len - 1; + } + case EOM_BIT: { + unsigned int tot_len = pin->tx_msg.len + + pin->tx_extra_bytes; + unsigned int tx_byte_idx = pin->tx_bit / 10; + + v = !pin->tx_post_eom && tx_byte_idx == tot_len - 1; + if (tot_len > 1 && tx_byte_idx == tot_len - 2 && + tx_early_eom(pin)) { + /* Error injection: set EOM one byte early */ + v = true; + pin->tx_post_eom = true; + } else if (v && tx_no_eom(pin)) { + /* Error injection: no EOM */ + v = false; + } pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW : - CEC_ST_TX_DATA_BIT_0_LOW; + CEC_ST_TX_DATA_BIT_0_LOW; break; - case 9: + } + case ACK_BIT: pin->state = CEC_ST_TX_DATA_BIT_1_LOW; break; } + if (tx_custom_bit(pin)) + pin->state = CEC_ST_TX_DATA_BIT_LOW_CUSTOM; cec_pin_low(pin); break; case CEC_ST_TX_DATA_BIT_0_LOW: case CEC_ST_TX_DATA_BIT_1_LOW: v = pin->state == CEC_ST_TX_DATA_BIT_1_LOW; - pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH : - CEC_ST_TX_DATA_BIT_0_HIGH; - is_ack_bit = pin->tx_bit % 10 == 9; - if (v && (pin->tx_bit < 4 || is_ack_bit)) + is_ack_bit = pin->tx_bit % 10 == ACK_BIT; + if (v && (pin->tx_bit < 4 || is_ack_bit)) { pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE; + } else if (!is_ack_bit && tx_short_bit(pin)) { + /* Error Injection: send an invalid (too short) bit */ + pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_SHORT : + CEC_ST_TX_DATA_BIT_0_HIGH_SHORT; + } else if (!is_ack_bit && tx_long_bit(pin)) { + /* Error Injection: send an invalid (too long) bit */ + pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_LONG : + CEC_ST_TX_DATA_BIT_0_HIGH_LONG; + } else { + pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH : + CEC_ST_TX_DATA_BIT_0_HIGH; + } + cec_pin_high(pin); + break; + + case CEC_ST_TX_DATA_BIT_LOW_CUSTOM: + pin->state = CEC_ST_TX_DATA_BIT_HIGH_CUSTOM; cec_pin_high(pin); break; case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE: /* Read the CEC value at the sample time */ v = cec_pin_read(pin); - is_ack_bit = pin->tx_bit % 10 == 9; + is_ack_bit = pin->tx_bit % 10 == ACK_BIT; /* * If v == 0 and we're within the first 4 bits * of the initiator, then someone else started @@ -258,7 +573,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) * transmitter has more leading 0 bits in the * initiator). */ - if (!v && !is_ack_bit) { + if (!v && !is_ack_bit && !pin->tx_generated_poll) { pin->tx_msg.len = 0; pin->work_tx_ts = ts; pin->work_tx_status = CEC_TX_STATUS_ARB_LOST; @@ -267,18 +582,27 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) pin->tx_bit = 0; memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg)); pin->rx_msg.msg[0] = pin->tx_msg.msg[0]; - pin->rx_msg.msg[0] &= ~(1 << (7 - pin->rx_bit)); + pin->rx_msg.msg[0] &= (0xff << (8 - pin->rx_bit)); pin->rx_msg.len = 0; + pin->ts = ktime_sub_us(ts, CEC_TIM_DATA_BIT_SAMPLE); pin->state = CEC_ST_RX_DATA_POST_SAMPLE; pin->rx_bit++; break; } pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE; + if (!is_ack_bit && tx_short_bit(pin)) { + /* Error Injection: send an invalid (too short) bit */ + pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT; + } else if (!is_ack_bit && tx_long_bit(pin)) { + /* Error Injection: send an invalid (too long) bit */ + pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG; + } if (!is_ack_bit) break; /* Was the message ACKed? */ ack = cec_msg_is_broadcast(&pin->tx_msg) ? v : !v; - if (!ack) { + if (!ack && !pin->tx_ignore_nack_until_eom && + pin->tx_bit / 10 < pin->tx_msg.len && !pin->tx_post_eom) { /* * Note: the CEC spec is ambiguous regarding * what action to take when a NACK appears @@ -295,6 +619,15 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) } break; + case CEC_ST_TX_PULSE_LOW_CUSTOM: + cec_pin_high(pin); + pin->state = CEC_ST_TX_PULSE_HIGH_CUSTOM; + break; + + case CEC_ST_TX_PULSE_HIGH_CUSTOM: + cec_pin_to_idle(pin); + break; + default: break; } @@ -322,6 +655,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) bool ack; bool bcast, for_us; u8 dest; + u8 poll; switch (pin->state) { /* Receive states */ @@ -331,24 +665,54 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) break; pin->state = CEC_ST_RX_START_BIT_HIGH; delta = ktime_us_delta(ts, pin->ts); - pin->ts = ts; /* Start bit low is too short, go back to idle */ - if (delta < CEC_TIM_START_BIT_LOW_MIN - - CEC_TIM_IDLE_SAMPLE) { + if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) { + if (!pin->rx_start_bit_low_too_short_cnt++) { + pin->rx_start_bit_low_too_short_ts = pin->ts; + pin->rx_start_bit_low_too_short_delta = delta; + } cec_pin_to_idle(pin); + break; + } + if (rx_arb_lost(pin, &poll)) { + cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf); + pin->tx_generated_poll = true; + pin->tx_extra_bytes = 0; + pin->state = CEC_ST_TX_START_BIT_HIGH; + pin->ts = ts; } break; case CEC_ST_RX_START_BIT_HIGH: v = cec_pin_read(pin); delta = ktime_us_delta(ts, pin->ts); - if (v && delta > CEC_TIM_START_BIT_TOTAL_MAX - - CEC_TIM_START_BIT_LOW_MIN) { + /* + * Unfortunately the spec does not specify when to give up + * and go to idle. We just pick TOTAL_LONG. + */ + if (v && delta > CEC_TIM_START_BIT_TOTAL_LONG) { + pin->rx_start_bit_too_long_cnt++; cec_pin_to_idle(pin); break; } if (v) break; + /* Start bit is too short, go back to idle */ + if (delta < CEC_TIM_START_BIT_TOTAL_MIN - CEC_TIM_IDLE_SAMPLE) { + if (!pin->rx_start_bit_too_short_cnt++) { + pin->rx_start_bit_too_short_ts = pin->ts; + pin->rx_start_bit_too_short_delta = delta; + } + cec_pin_to_idle(pin); + break; + } + if (rx_low_drive(pin)) { + /* Error injection: go to low drive */ + cec_pin_low(pin); + pin->state = CEC_ST_RX_LOW_DRIVE; + pin->rx_low_drive_cnt++; + break; + } pin->state = CEC_ST_RX_DATA_SAMPLE; pin->ts = ts; pin->rx_eom = false; @@ -363,36 +727,55 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) pin->rx_msg.msg[pin->rx_bit / 10] |= v << (7 - (pin->rx_bit % 10)); break; - case 8: + case EOM_BIT: pin->rx_eom = v; pin->rx_msg.len = pin->rx_bit / 10 + 1; break; - case 9: + case ACK_BIT: break; } pin->rx_bit++; break; case CEC_ST_RX_DATA_POST_SAMPLE: - pin->state = CEC_ST_RX_DATA_HIGH; + pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW; break; - case CEC_ST_RX_DATA_HIGH: + case CEC_ST_RX_DATA_WAIT_FOR_LOW: v = cec_pin_read(pin); delta = ktime_us_delta(ts, pin->ts); - if (v && delta > CEC_TIM_DATA_BIT_TOTAL_MAX) { + /* + * Unfortunately the spec does not specify when to give up + * and go to idle. We just pick TOTAL_LONG. + */ + if (v && delta > CEC_TIM_DATA_BIT_TOTAL_LONG) { + pin->rx_data_bit_too_long_cnt++; cec_pin_to_idle(pin); break; } if (v) break; + + if (rx_low_drive(pin)) { + /* Error injection: go to low drive */ + cec_pin_low(pin); + pin->state = CEC_ST_RX_LOW_DRIVE; + pin->rx_low_drive_cnt++; + break; + } + /* * Go to low drive state when the total bit time is * too short. */ if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) { + if (!pin->rx_data_bit_too_short_cnt++) { + pin->rx_data_bit_too_short_ts = pin->ts; + pin->rx_data_bit_too_short_delta = delta; + } cec_pin_low(pin); - pin->state = CEC_ST_LOW_DRIVE; + pin->state = CEC_ST_RX_LOW_DRIVE; + pin->rx_low_drive_cnt++; break; } pin->ts = ts; @@ -408,6 +791,11 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) /* ACK bit value */ ack = bcast ? 1 : !for_us; + if (for_us && rx_nack(pin)) { + /* Error injection: toggle the ACK bit */ + ack = !ack; + } + if (ack) { /* No need to write to the bus, just wait */ pin->state = CEC_ST_RX_ACK_HIGH_POST; @@ -434,7 +822,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) break; } pin->rx_bit++; - pin->state = CEC_ST_RX_DATA_HIGH; + pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW; break; case CEC_ST_RX_ACK_FINISH: @@ -456,6 +844,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) struct cec_adapter *adap = pin->adap; ktime_t ts; s32 delta; + u32 usecs; ts = ktime_get(); if (ktime_to_ns(pin->timer_ts)) { @@ -503,13 +892,27 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) /* Transmit states */ case CEC_ST_TX_WAIT_FOR_HIGH: case CEC_ST_TX_START_BIT_LOW: - case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE: - case CEC_ST_TX_DATA_BIT_0_HIGH: - case CEC_ST_TX_DATA_BIT_1_HIGH: case CEC_ST_TX_START_BIT_HIGH: + case CEC_ST_TX_START_BIT_HIGH_SHORT: + case CEC_ST_TX_START_BIT_HIGH_LONG: + case CEC_ST_TX_START_BIT_LOW_CUSTOM: + case CEC_ST_TX_START_BIT_HIGH_CUSTOM: case CEC_ST_TX_DATA_BIT_0_LOW: + case CEC_ST_TX_DATA_BIT_0_HIGH: + case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT: + case CEC_ST_TX_DATA_BIT_0_HIGH_LONG: case CEC_ST_TX_DATA_BIT_1_LOW: + case CEC_ST_TX_DATA_BIT_1_HIGH: + case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT: + case CEC_ST_TX_DATA_BIT_1_HIGH_LONG: case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE: + case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE: + case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT: + case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG: + case CEC_ST_TX_DATA_BIT_LOW_CUSTOM: + case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM: + case CEC_ST_TX_PULSE_LOW_CUSTOM: + case CEC_ST_TX_PULSE_HIGH_CUSTOM: cec_pin_tx_states(pin, ts); break; @@ -518,7 +921,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) case CEC_ST_RX_START_BIT_HIGH: case CEC_ST_RX_DATA_SAMPLE: case CEC_ST_RX_DATA_POST_SAMPLE: - case CEC_ST_RX_DATA_HIGH: + case CEC_ST_RX_DATA_WAIT_FOR_LOW: case CEC_ST_RX_ACK_LOW: case CEC_ST_RX_ACK_LOW_POST: case CEC_ST_RX_ACK_HIGH_POST: @@ -545,7 +948,10 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) if (delta / CEC_TIM_DATA_BIT_TOTAL > pin->tx_signal_free_time) { pin->tx_nacked = false; - pin->state = CEC_ST_TX_START_BIT_LOW; + if (tx_custom_start(pin)) + pin->state = CEC_ST_TX_START_BIT_LOW_CUSTOM; + else + pin->state = CEC_ST_TX_START_BIT_LOW; /* Generate start bit */ cec_pin_low(pin); break; @@ -555,6 +961,13 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) pin->state = CEC_ST_TX_WAIT; break; } + if (pin->tx_custom_pulse && pin->state == CEC_ST_IDLE) { + pin->tx_custom_pulse = false; + /* Generate custom pulse */ + cec_pin_low(pin); + pin->state = CEC_ST_TX_PULSE_LOW_CUSTOM; + break; + } if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL || pin->enable_irq_failed || adap->is_configuring || adap->is_configured || adap->monitor_all_cnt) @@ -565,21 +978,40 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) wake_up_interruptible(&pin->kthread_waitq); return HRTIMER_NORESTART; - case CEC_ST_LOW_DRIVE: + case CEC_ST_TX_LOW_DRIVE: + case CEC_ST_RX_LOW_DRIVE: + cec_pin_high(pin); cec_pin_to_idle(pin); break; default: break; } - if (!adap->monitor_pin_cnt || states[pin->state].usecs <= 150) { + + switch (pin->state) { + case CEC_ST_TX_START_BIT_LOW_CUSTOM: + case CEC_ST_TX_DATA_BIT_LOW_CUSTOM: + case CEC_ST_TX_PULSE_LOW_CUSTOM: + usecs = pin->tx_custom_low_usecs; + break; + case CEC_ST_TX_START_BIT_HIGH_CUSTOM: + case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM: + case CEC_ST_TX_PULSE_HIGH_CUSTOM: + usecs = pin->tx_custom_high_usecs; + break; + default: + usecs = states[pin->state].usecs; + break; + } + + if (!adap->monitor_pin_cnt || usecs <= 150) { pin->wait_usecs = 0; - pin->timer_ts = ktime_add_us(ts, states[pin->state].usecs); + pin->timer_ts = ktime_add_us(ts, usecs); hrtimer_forward_now(timer, - ns_to_ktime(states[pin->state].usecs * 1000)); + ns_to_ktime(usecs * 1000)); return HRTIMER_RESTART; } - pin->wait_usecs = states[pin->state].usecs - 100; + pin->wait_usecs = usecs - 100; pin->timer_ts = ktime_add_us(ts, 100); hrtimer_forward_now(timer, ns_to_ktime(100000)); return HRTIMER_RESTART; @@ -596,12 +1028,25 @@ static int cec_pin_thread_func(void *_adap) pin->work_rx_msg.len || pin->work_tx_status || atomic_read(&pin->work_irq_change) || - atomic_read(&pin->work_pin_events)); + atomic_read(&pin->work_pin_num_events)); if (pin->work_rx_msg.len) { - cec_received_msg_ts(adap, &pin->work_rx_msg, + struct cec_msg *msg = &pin->work_rx_msg; + + if (msg->len > 1 && msg->len < CEC_MAX_MSG_SIZE && + rx_add_byte(pin)) { + /* Error injection: add byte to the message */ + msg->msg[msg->len++] = 0x55; + } + if (msg->len > 2 && rx_remove_byte(pin)) { + /* Error injection: remove byte from message */ + msg->len--; + } + if (msg->len > CEC_MAX_MSG_SIZE) + msg->len = CEC_MAX_MSG_SIZE; + cec_received_msg_ts(adap, msg, ns_to_ktime(pin->work_rx_msg.rx_ts)); - pin->work_rx_msg.len = 0; + msg->len = 0; } if (pin->work_tx_status) { unsigned int tx_status = pin->work_tx_status; @@ -611,14 +1056,16 @@ static int cec_pin_thread_func(void *_adap) pin->work_tx_ts); } - while (atomic_read(&pin->work_pin_events)) { + while (atomic_read(&pin->work_pin_num_events)) { unsigned int idx = pin->work_pin_events_rd; + u8 v = pin->work_pin_events[idx]; cec_queue_pin_cec_event(adap, - pin->work_pin_is_high[idx], + v & CEC_PIN_EVENT_FL_IS_HIGH, + v & CEC_PIN_EVENT_FL_DROPPED, pin->work_pin_ts[idx]); pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS; - atomic_dec(&pin->work_pin_events); + atomic_dec(&pin->work_pin_num_events); } switch (atomic_xchg(&pin->work_irq_change, @@ -654,8 +1101,9 @@ static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable) pin->enabled = enable; if (enable) { - atomic_set(&pin->work_pin_events, 0); + atomic_set(&pin->work_pin_num_events, 0); pin->work_pin_events_rd = pin->work_pin_events_wr = 0; + pin->work_pin_events_dropped = false; cec_pin_read(pin); cec_pin_to_idle(pin); pin->tx_msg.len = 0; @@ -692,23 +1140,37 @@ static int cec_pin_adap_log_addr(struct cec_adapter *adap, u8 log_addr) return 0; } +void cec_pin_start_timer(struct cec_pin *pin) +{ + if (pin->state != CEC_ST_RX_IRQ) + return; + + atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED); + pin->ops->disable_irq(pin->adap); + cec_pin_high(pin); + cec_pin_to_idle(pin); + hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL); +} + static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct cec_pin *pin = adap->pin; pin->tx_signal_free_time = signal_free_time; + pin->tx_extra_bytes = 0; pin->tx_msg = *msg; + if (msg->len > 1) { + /* Error injection: add byte to the message */ + pin->tx_extra_bytes = tx_add_bytes(pin); + } + if (msg->len > 2 && tx_remove_byte(pin)) { + /* Error injection: remove byte from the message */ + pin->tx_msg.len--; + } pin->work_tx_status = 0; pin->tx_bit = 0; - if (pin->state == CEC_ST_RX_IRQ) { - atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED); - pin->ops->disable_irq(adap); - cec_pin_high(pin); - cec_pin_to_idle(pin); - hrtimer_start(&pin->timer, ns_to_ktime(0), - HRTIMER_MODE_REL); - } + cec_pin_start_timer(pin); return 0; } @@ -717,10 +1179,12 @@ static void cec_pin_adap_status(struct cec_adapter *adap, { struct cec_pin *pin = adap->pin; - seq_printf(file, "state: %s\n", states[pin->state].name); - seq_printf(file, "tx_bit: %d\n", pin->tx_bit); - seq_printf(file, "rx_bit: %d\n", pin->rx_bit); + seq_printf(file, "state: %s\n", states[pin->state].name); + seq_printf(file, "tx_bit: %d\n", pin->tx_bit); + seq_printf(file, "rx_bit: %d\n", pin->rx_bit); seq_printf(file, "cec pin: %d\n", pin->ops->read(adap)); + seq_printf(file, "cec pin events dropped: %u\n", + pin->work_pin_events_dropped_cnt); seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed); if (pin->timer_100ms_overruns) { seq_printf(file, "timer overruns > 100ms: %u of %u\n", @@ -732,11 +1196,45 @@ static void cec_pin_adap_status(struct cec_adapter *adap, seq_printf(file, "avg timer overrun: %u usecs\n", pin->timer_sum_overrun / pin->timer_100ms_overruns); } + if (pin->rx_start_bit_low_too_short_cnt) + seq_printf(file, + "rx start bit low too short: %u (delta %u, ts %llu)\n", + pin->rx_start_bit_low_too_short_cnt, + pin->rx_start_bit_low_too_short_delta, + pin->rx_start_bit_low_too_short_ts); + if (pin->rx_start_bit_too_short_cnt) + seq_printf(file, + "rx start bit too short: %u (delta %u, ts %llu)\n", + pin->rx_start_bit_too_short_cnt, + pin->rx_start_bit_too_short_delta, + pin->rx_start_bit_too_short_ts); + if (pin->rx_start_bit_too_long_cnt) + seq_printf(file, "rx start bit too long: %u\n", + pin->rx_start_bit_too_long_cnt); + if (pin->rx_data_bit_too_short_cnt) + seq_printf(file, + "rx data bit too short: %u (delta %u, ts %llu)\n", + pin->rx_data_bit_too_short_cnt, + pin->rx_data_bit_too_short_delta, + pin->rx_data_bit_too_short_ts); + if (pin->rx_data_bit_too_long_cnt) + seq_printf(file, "rx data bit too long: %u\n", + pin->rx_data_bit_too_long_cnt); + seq_printf(file, "rx initiated low drive: %u\n", pin->rx_low_drive_cnt); + seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt); + pin->work_pin_events_dropped_cnt = 0; pin->timer_cnt = 0; pin->timer_100ms_overruns = 0; pin->timer_300ms_overruns = 0; pin->timer_max_overrun = 0; pin->timer_sum_overrun = 0; + pin->rx_start_bit_low_too_short_cnt = 0; + pin->rx_start_bit_too_short_cnt = 0; + pin->rx_start_bit_too_long_cnt = 0; + pin->rx_data_bit_too_short_cnt = 0; + pin->rx_data_bit_too_long_cnt = 0; + pin->rx_low_drive_cnt = 0; + pin->tx_low_drive_cnt = 0; if (pin->ops->status) pin->ops->status(adap, file); } @@ -778,6 +1276,10 @@ static const struct cec_adap_ops cec_pin_adap_ops = { .adap_transmit = cec_pin_adap_transmit, .adap_status = cec_pin_adap_status, .adap_free = cec_pin_adap_free, +#ifdef CONFIG_CEC_PIN_ERROR_INJ + .error_inj_parse_line = cec_pin_error_inj_parse_line, + .error_inj_show = cec_pin_error_inj_show, +#endif }; struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, @@ -792,6 +1294,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, hrtimer_init(&pin->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); pin->timer.function = cec_pin_timer; init_waitqueue_head(&pin->kthread_waitq); + pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT; + pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT; adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name, caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN, diff --git a/drivers/media/cec/cec-priv.h b/drivers/media/cec/cec-priv.h index daf597643af8..804e38f849c7 100644 --- a/drivers/media/cec/cec-priv.h +++ b/drivers/media/cec/cec-priv.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cec-priv.h - HDMI Consumer Electronics Control internal header * * 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. */ #ifndef _CEC_PRIV_H diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index c5c827e11b64..b5dcc6d1fe90 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -631,7 +631,8 @@ smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, cb->p = buffer; cb->offset_in_common = buffer - (u8 *) common_buffer; - cb->phys = common_buffer_phys + cb->offset_in_common; + if (common_buffer_phys) + cb->phys = common_buffer_phys + cb->offset_in_common; return cb; } @@ -690,17 +691,21 @@ int smscore_register_device(struct smsdevice_params_t *params, /* alloc common buffer */ dev->common_buffer_size = params->buffer_size * params->num_buffers; - dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, - &dev->common_buffer_phys, - GFP_KERNEL | GFP_DMA); - if (!dev->common_buffer) { + if (params->usb_device) + buffer = kzalloc(dev->common_buffer_size, GFP_KERNEL); + else + buffer = dma_alloc_coherent(params->device, + dev->common_buffer_size, + &dev->common_buffer_phys, + GFP_KERNEL | GFP_DMA); + if (!buffer) { smscore_unregister_device(dev); return -ENOMEM; } + dev->common_buffer = buffer; /* prepare dma buffers */ - for (buffer = dev->common_buffer; - dev->num_buffers < params->num_buffers; + for (; dev->num_buffers < params->num_buffers; dev->num_buffers++, buffer += params->buffer_size) { struct smscore_buffer_t *cb; @@ -720,6 +725,7 @@ int smscore_register_device(struct smsdevice_params_t *params, dev->board_id = SMS_BOARD_UNKNOWN; dev->context = params->context; dev->device = params->device; + dev->usb_device = params->usb_device; dev->setmode_handler = params->setmode_handler; dev->detectmode_handler = params->detectmode_handler; dev->sendrequest_handler = params->sendrequest_handler; @@ -1231,10 +1237,15 @@ void smscore_unregister_device(struct smscore_device_t *coredev) pr_debug("freed %d buffers\n", num_buffers); - if (coredev->common_buffer) - dma_free_coherent(NULL, coredev->common_buffer_size, - coredev->common_buffer, coredev->common_buffer_phys); - + if (coredev->common_buffer) { + if (coredev->usb_device) + kfree(coredev->common_buffer); + else + dma_free_coherent(coredev->device, + coredev->common_buffer_size, + coredev->common_buffer, + coredev->common_buffer_phys); + } kfree(coredev->fw_buf); list_del(&coredev->entry); diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 4cc39e4a8318..134c69f7ea7b 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -134,6 +134,7 @@ struct smscore_buffer_t { struct smsdevice_params_t { struct device *device; + struct usb_device *usb_device; int buffer_size; int num_buffers; @@ -176,6 +177,7 @@ struct smscore_device_t { void *context; struct device *device; + struct usb_device *usb_device; char devpath[32]; unsigned long device_flags; diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c index 43180204fab2..3a3dc23c560c 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * v4l2-tpg-colors.c - A table that converts colors to various colorspaces * @@ -20,19 +21,6 @@ * in order to preserve precision. * * Copyright 2014 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 <linux/videodev2.h> diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 2b3d4ac4dfd4..37632bc524d4 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * v4l2-tpg-core.c - Test Pattern Generator * @@ -5,19 +6,6 @@ * vivi.c source for the copyright information of those functions. * * Copyright 2014 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 <linux/module.h> @@ -1155,13 +1143,13 @@ static void gen_twopix(struct tpg_data *tpg, case V4L2_PIX_FMT_NV24: buf[0][offset] = r_y_h; buf[1][2 * offset] = g_u_s; - buf[1][2 * offset + 1] = b_v; + buf[1][(2 * offset + 1) % 8] = b_v; break; case V4L2_PIX_FMT_NV42: buf[0][offset] = r_y_h; buf[1][2 * offset] = b_v; - buf[1][2 * offset + 1] = g_u_s; + buf[1][(2 * offset + 1) %8] = g_u_s; break; case V4L2_PIX_FMT_YUYV: diff --git a/drivers/media/common/videobuf2/Kconfig b/drivers/media/common/videobuf2/Kconfig index 5df05250de94..17c32ea58395 100644 --- a/drivers/media/common/videobuf2/Kconfig +++ b/drivers/media/common/videobuf2/Kconfig @@ -3,6 +3,9 @@ config VIDEOBUF2_CORE select DMA_SHARED_BUFFER tristate +config VIDEOBUF2_V4L2 + tristate + config VIDEOBUF2_MEMOPS tristate select FRAME_VECTOR diff --git a/drivers/media/common/videobuf2/Makefile b/drivers/media/common/videobuf2/Makefile index 19de5ccda20b..77bebe8b202f 100644 --- a/drivers/media/common/videobuf2/Makefile +++ b/drivers/media/common/videobuf2/Makefile @@ -1,5 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +videobuf2-common-objs := videobuf2-core.o -obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o videobuf2-v4l2.o +ifeq ($(CONFIG_TRACEPOINTS),y) + videobuf2-common-objs += vb2-trace.o +endif + +obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-common.o +obj-$(CONFIG_VIDEOBUF2_V4L2) += videobuf2-v4l2.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o diff --git a/drivers/media/v4l2-core/vb2-trace.c b/drivers/media/common/videobuf2/vb2-trace.c index 4c0f39d271f0..4c0f39d271f0 100644 --- a/drivers/media/v4l2-core/vb2-trace.c +++ b/drivers/media/common/videobuf2/vb2-trace.c diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index debe35fc66b4..d3f7bb33a54d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1696,6 +1696,15 @@ static void __vb2_queue_cancel(struct vb2_queue *q) for (i = 0; i < q->num_buffers; ++i) { struct vb2_buffer *vb = q->bufs[i]; + if (vb->state == VB2_BUF_STATE_PREPARED || + vb->state == VB2_BUF_STATE_QUEUED) { + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, + vb->planes[plane].mem_priv); + } + if (vb->state != VB2_BUF_STATE_DEQUEUED) { vb->state = VB2_BUF_STATE_PREPARED; call_void_vb_qop(vb, buf_finish, vb); diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index 3a7c80cd1a17..359fb9804d16 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -106,7 +106,7 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr, if (nums[i-1] + 1 != nums[i]) goto fail_map; buf->vaddr = (__force void *) - ioremap_nocache(nums[0] << PAGE_SHIFT, size); + ioremap_nocache(__pfn_to_phys(nums[0]), size + offset); } else { buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); diff --git a/drivers/media/dvb-core/Makefile b/drivers/media/dvb-core/Makefile index 3a105d82019a..62b028ded9f7 100644 --- a/drivers/media/dvb-core/Makefile +++ b/drivers/media/dvb-core/Makefile @@ -4,7 +4,7 @@ # dvb-net-$(CONFIG_DVB_NET) := dvb_net.o -dvb-vb2-$(CONFIG_DVB_MMSP) := dvb_vb2.o +dvb-vb2-$(CONFIG_DVB_MMAP) := dvb_vb2.o dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o \ dvb_ca_en50221.o dvb_frontend.o \ diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 6d53af00190e..61a750fae465 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -128,11 +128,7 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; struct dmx_frontend *front; -#ifndef DVB_MMAP bool need_ringbuffer = false; -#else - const bool need_ringbuffer = true; -#endif dprintk("%s\n", __func__); @@ -144,17 +140,31 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) return -ENODEV; } -#ifndef DVB_MMAP + dmxdev->may_do_mmap = 0; + + /* + * The logic here is a little tricky due to the ifdef. + * + * The ringbuffer is used for both read and mmap. + * + * It is not needed, however, on two situations: + * - Write devices (access with O_WRONLY); + * - For duplex device nodes, opened with O_RDWR. + */ + if ((file->f_flags & O_ACCMODE) == O_RDONLY) need_ringbuffer = true; -#else - if ((file->f_flags & O_ACCMODE) == O_RDWR) { + else if ((file->f_flags & O_ACCMODE) == O_RDWR) { if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) { +#ifdef CONFIG_DVB_MMAP + dmxdev->may_do_mmap = 1; + need_ringbuffer = true; +#else mutex_unlock(&dmxdev->mutex); return -EOPNOTSUPP; +#endif } } -#endif if (need_ringbuffer) { void *mem; @@ -169,8 +179,9 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) return -ENOMEM; } dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); - dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr", - file->f_flags & O_NONBLOCK); + if (dmxdev->may_do_mmap) + dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr", + file->f_flags & O_NONBLOCK); dvbdev->readers--; } @@ -200,11 +211,6 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; -#ifndef DVB_MMAP - bool need_ringbuffer = false; -#else - const bool need_ringbuffer = true; -#endif mutex_lock(&dmxdev->mutex); @@ -213,15 +219,14 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) dmxdev->demux->connect_frontend(dmxdev->demux, dmxdev->dvr_orig_fe); } -#ifndef DVB_MMAP - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - need_ringbuffer = true; -#endif - if (need_ringbuffer) { - if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx)) - dvb_vb2_stream_off(&dmxdev->dvr_vb2_ctx); - dvb_vb2_release(&dmxdev->dvr_vb2_ctx); + if (((file->f_flags & O_ACCMODE) == O_RDONLY) || + dmxdev->may_do_mmap) { + if (dmxdev->may_do_mmap) { + if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx)) + dvb_vb2_stream_off(&dmxdev->dvr_vb2_ctx); + dvb_vb2_release(&dmxdev->dvr_vb2_ctx); + } dvbdev->readers++; if (dmxdev->dvr_buffer.data) { void *mem = dmxdev->dvr_buffer.data; @@ -380,7 +385,8 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter) static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_section_filter *filter) + struct dmx_section_filter *filter, + u32 *buffer_flags) { struct dmxdev_filter *dmxdevfilter = filter->priv; int ret; @@ -399,10 +405,12 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, dprintk("section callback %*ph\n", 6, buffer1); if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) { ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx, - buffer1, buffer1_len); + buffer1, buffer1_len, + buffer_flags); if (ret == buffer1_len) ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx, - buffer2, buffer2_len); + buffer2, buffer2_len, + buffer_flags); } else { ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len); @@ -422,11 +430,12 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_ts_feed *feed) + struct dmx_ts_feed *feed, + u32 *buffer_flags) { struct dmxdev_filter *dmxdevfilter = feed->priv; struct dvb_ringbuffer *buffer; -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP struct dvb_vb2_ctx *ctx; #endif int ret; @@ -440,20 +449,22 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, if (dmxdevfilter->params.pes.output == DMX_OUT_TAP || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) { buffer = &dmxdevfilter->buffer; -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP ctx = &dmxdevfilter->vb2_ctx; #endif } else { buffer = &dmxdevfilter->dev->dvr_buffer; -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP ctx = &dmxdevfilter->dev->dvr_vb2_ctx; #endif } if (dvb_vb2_is_streaming(ctx)) { - ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len); + ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len, + buffer_flags); if (ret == buffer1_len) - ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len); + ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len, + buffer_flags); } else { if (buffer->error) { spin_unlock(&dmxdevfilter->dev->lock); @@ -802,6 +813,12 @@ static int dvb_demux_open(struct inode *inode, struct file *file) mutex_init(&dmxdevfilter->mutex); file->private_data = dmxdevfilter; +#ifdef CONFIG_DVB_MMAP + dmxdev->may_do_mmap = 1; +#else + dmxdev->may_do_mmap = 0; +#endif + dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); dvb_vb2_init(&dmxdevfilter->vb2_ctx, "demux_filter", file->f_flags & O_NONBLOCK); @@ -1111,7 +1128,7 @@ static int dvb_demux_do_ioctl(struct file *file, mutex_unlock(&dmxdevfilter->mutex); break; -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP case DMX_REQBUFS: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { mutex_unlock(&dmxdev->mutex); @@ -1160,7 +1177,7 @@ static int dvb_demux_do_ioctl(struct file *file, break; #endif default: - ret = -EINVAL; + ret = -ENOTTY; break; } mutex_unlock(&dmxdev->mutex); @@ -1199,13 +1216,16 @@ static __poll_t dvb_demux_poll(struct file *file, poll_table *wait) return mask; } -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma) { struct dmxdev_filter *dmxdevfilter = file->private_data; struct dmxdev *dmxdev = dmxdevfilter->dev; int ret; + if (!dmxdev->may_do_mmap) + return -ENOTTY; + if (mutex_lock_interruptible(&dmxdev->mutex)) return -ERESTARTSYS; @@ -1249,7 +1269,7 @@ static const struct file_operations dvb_demux_fops = { .release = dvb_demux_release, .poll = dvb_demux_poll, .llseek = default_llseek, -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP .mmap = dvb_demux_mmap, #endif }; @@ -1280,7 +1300,7 @@ static int dvb_dvr_do_ioctl(struct file *file, ret = dvb_dvr_set_buffer_size(dmxdev, arg); break; -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP case DMX_REQBUFS: ret = dvb_vb2_reqbufs(&dmxdev->dvr_vb2_ctx, parg); break; @@ -1304,7 +1324,7 @@ static int dvb_dvr_do_ioctl(struct file *file, break; #endif default: - ret = -EINVAL; + ret = -ENOTTY; break; } mutex_unlock(&dmxdev->mutex); @@ -1322,11 +1342,6 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait) struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; __poll_t mask = 0; -#ifndef DVB_MMAP - bool need_ringbuffer = false; -#else - const bool need_ringbuffer = true; -#endif dprintk("%s\n", __func__); @@ -1337,11 +1352,8 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait) poll_wait(file, &dmxdev->dvr_buffer.queue, wait); -#ifndef DVB_MMAP - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - need_ringbuffer = true; -#endif - if (need_ringbuffer) { + if (((file->f_flags & O_ACCMODE) == O_RDONLY) || + dmxdev->may_do_mmap) { if (dmxdev->dvr_buffer.error) mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI | EPOLLERR); @@ -1353,13 +1365,16 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait) return mask; } -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma) { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; int ret; + if (!dmxdev->may_do_mmap) + return -ENOTTY; + if (dmxdev->exit) return -ENODEV; @@ -1381,7 +1396,7 @@ static const struct file_operations dvb_dvr_fops = { .release = dvb_dvr_release, .poll = dvb_dvr_poll, .llseek = default_llseek, -#ifdef DVB_MMAP +#ifdef CONFIG_DVB_MMAP .mmap = dvb_dvr_mmap, #endif }; diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 204d0f6c678d..97365a863519 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1254,8 +1254,8 @@ static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca, ca->pub->slot_ts_enable(ca->pub, slot); sl->slot_state = DVB_CA_SLOTSTATE_RUNNING; dvb_ca_en50221_thread_update_delay(ca); - pr_err("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", - ca->dvbdev->adapter->num); + pr_info("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", + ca->dvbdev->adapter->num); break; case DVB_CA_SLOTSTATE_RUNNING: diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 210eed0269b0..f45091246bdc 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -55,6 +55,17 @@ MODULE_PARM_DESC(dvb_demux_feed_err_pkts, dprintk(x); \ } while (0) +#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG +# define dprintk_sect_loss(x...) dprintk(x) +#else +# define dprintk_sect_loss(x...) +#endif + +#define set_buf_flags(__feed, __flag) \ + do { \ + (__feed)->buffer_flags |= (__flag); \ + } while (0) + /****************************************************************************** * static inlined helper functions ******************************************************************************/ @@ -104,31 +115,30 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, { int count = payload(buf); int p; -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG int ccok; u8 cc; -#endif if (count == 0) return -1; p = 188 - count; -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG cc = buf[3] & 0x0f; ccok = ((feed->cc + 1) & 0x0f) == cc; feed->cc = cc; - if (!ccok) - dprintk("missed packet: %d instead of %d!\n", - cc, (feed->cc + 1) & 0x0f); -#endif + if (!ccok) { + set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("missed packet: %d instead of %d!\n", + cc, (feed->cc + 1) & 0x0f); + } if (buf[1] & 0x40) // PUSI ? feed->peslen = 0xfffa; feed->peslen += count; - return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts); + return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, + &feed->buffer_flags); } static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, @@ -150,7 +160,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, return 0; return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, - NULL, 0, &f->filter); + NULL, 0, &f->filter, &feed->buffer_flags); } static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) @@ -169,8 +179,10 @@ static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) if (sec->check_crc) { section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0); if (section_syntax_indicator && - demux->check_crc32(feed, sec->secbuf, sec->seclen)) + demux->check_crc32(feed, sec->secbuf, sec->seclen)) { + set_buf_flags(feed, DMX_BUFFER_FLAG_HAD_CRC32_DISCARD); return -1; + } } do { @@ -187,7 +199,6 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) { struct dmx_section_feed *sec = &feed->feed.sec; -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG if (sec->secbufp < sec->tsfeedp) { int n = sec->tsfeedp - sec->secbufp; @@ -197,12 +208,13 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) * but just first and last. */ if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) { - dprintk("section ts padding loss: %d/%d\n", - n, sec->tsfeedp); - dprintk("pad data: %*ph\n", n, sec->secbuf); + set_buf_flags(feed, + DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("section ts padding loss: %d/%d\n", + n, sec->tsfeedp); + dprintk_sect_loss("pad data: %*ph\n", n, sec->secbuf); } } -#endif sec->tsfeedp = sec->secbufp = sec->seclen = 0; sec->secbuf = sec->secbuf_base; @@ -237,11 +249,10 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, return 0; if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) { -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG - dprintk("section buffer full loss: %d/%d\n", - sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, - DMX_MAX_SECFEED_SIZE); -#endif + set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("section buffer full loss: %d/%d\n", + sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, + DMX_MAX_SECFEED_SIZE); len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp; } @@ -269,12 +280,13 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, sec->seclen = seclen; sec->crc_val = ~0; /* dump [secbuf .. secbuf+seclen) */ - if (feed->pusi_seen) + if (feed->pusi_seen) { dvb_dmx_swfilter_section_feed(feed); -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG - else - dprintk("pusi not seen, discarding section data\n"); -#endif + } else { + set_buf_flags(feed, + DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("pusi not seen, discarding section data\n"); + } sec->secbufp += seclen; /* secbufp and secbuf moving together is */ sec->secbuf += seclen; /* redundant but saves pointer arithmetic */ } @@ -307,18 +319,22 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, } if (!ccok || dc_i) { -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG - if (dc_i) - dprintk("%d frame with disconnect indicator\n", + if (dc_i) { + set_buf_flags(feed, + DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR); + dprintk_sect_loss("%d frame with disconnect indicator\n", cc); - else - dprintk("discontinuity: %d instead of %d. %d bytes lost\n", + } else { + set_buf_flags(feed, + DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("discontinuity: %d instead of %d. %d bytes lost\n", cc, (feed->cc + 1) & 0x0f, count + 4); + } /* - * those bytes under sume circumstances will again be reported + * those bytes under some circumstances will again be reported * in the following dvb_dmx_swfilter_section_new */ -#endif + /* * Discontinuity detected. Reset pusi_seen to * stop feeding of suspicious data until next PUSI=1 arrives @@ -326,6 +342,7 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, * FIXME: does it make sense if the MPEG-TS is the one * reporting discontinuity? */ + feed->pusi_seen = false; dvb_dmx_swfilter_section_new(feed); } @@ -345,11 +362,11 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, dvb_dmx_swfilter_section_new(feed); dvb_dmx_swfilter_section_copy_dump(feed, after, after_len); + } else if (count > 0) { + set_buf_flags(feed, + DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); + dprintk_sect_loss("PUSI=1 but %d bytes lost\n", count); } -#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG - else if (count > 0) - dprintk("PUSI=1 but %d bytes lost\n", count); -#endif } else { /* PUSI=0 (is not set), no section boundary */ dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count); @@ -369,7 +386,8 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, if (feed->ts_type & TS_PAYLOAD_ONLY) dvb_dmx_swfilter_payload(feed, buf); else - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts); + feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, + &feed->buffer_flags); } /* Used only on full-featured devices */ if (feed->ts_type & TS_DECODER) @@ -430,6 +448,11 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) } if (buf[1] & 0x80) { + list_for_each_entry(feed, &demux->feed_list, list_head) { + if ((feed->pid != pid) && (feed->pid != 0x2000)) + continue; + set_buf_flags(feed, DMX_BUFFER_FLAG_TEI); + } dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n", pid, buf[1]); /* data in this packet can't be trusted - drop it unless @@ -445,6 +468,13 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) (demux->cnt_storage[pid] + 1) & 0xf; if ((buf[3] & 0xf) != demux->cnt_storage[pid]) { + list_for_each_entry(feed, &demux->feed_list, list_head) { + if ((feed->pid != pid) && (feed->pid != 0x2000)) + continue; + set_buf_flags(feed, + DMX_BUFFER_PKT_COUNTER_MISMATCH); + } + dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n", pid, demux->cnt_storage[pid], buf[3] & 0xf); @@ -466,7 +496,8 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) if (feed->pid == pid) dvb_dmx_swfilter_packet_type(feed, buf); else if (feed->pid == 0x2000) - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts); + feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, + &feed->buffer_flags); } } @@ -585,7 +616,8 @@ void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) spin_lock_irqsave(&demux->lock, flags); - demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts); + demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, + &demux->feed->buffer_flags); spin_unlock_irqrestore(&demux->lock, flags); } @@ -785,6 +817,7 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx, feed->demux = demux; feed->pid = 0xffff; feed->peslen = 0xfffa; + feed->buffer_flags = 0; (*ts_feed) = &feed->feed.ts; (*ts_feed)->parent = dmx; @@ -1042,6 +1075,7 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux, dvbdmxfeed->cb.sec = callback; dvbdmxfeed->demux = dvbdmx; dvbdmxfeed->pid = 0xffff; + dvbdmxfeed->buffer_flags = 0; dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0; dvbdmxfeed->feed.sec.tsfeedp = 0; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index a7ed16e0841d..21a7d4b47e1a 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2294,7 +2294,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp)); if (IS_ERR(tvp)) return PTR_ERR(tvp); @@ -2328,7 +2328,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp)); if (IS_ERR(tvp)) return PTR_ERR(tvp); diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index b6c7eec863b9..ba39f9942e1d 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -883,7 +883,8 @@ static void dvb_net_ule(struct net_device *dev, const u8 *buf, size_t buf_len) static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_ts_feed *feed) + struct dmx_ts_feed *feed, + u32 *buffer_flags) { struct net_device *dev = feed->priv; @@ -992,7 +993,7 @@ static void dvb_net_sec(struct net_device *dev, static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_section_filter *filter) + struct dmx_section_filter *filter, u32 *buffer_flags) { struct net_device *dev = filter->priv; diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 763145d74e83..b811adf88afa 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -256,7 +256,8 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx) } int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx, - const unsigned char *src, int len) + const unsigned char *src, int len, + enum dmx_buffer_flags *buffer_flags) { unsigned long flags = 0; void *vbuf = NULL; @@ -264,15 +265,17 @@ int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx, unsigned char *psrc = (unsigned char *)src; int ll = 0; - dprintk(3, "[%s] %d bytes are rcvd\n", ctx->name, len); - if (!src) { - dprintk(3, "[%s]:NULL pointer src\n", ctx->name); - /**normal case: This func is called twice from demux driver - * once with valid src pointer, second time with NULL pointer - */ + /* + * normal case: This func is called twice from demux driver + * one with valid src pointer, second time with NULL pointer + */ + if (!src || !len) return 0; - } spin_lock_irqsave(&ctx->slock, flags); + if (buffer_flags && *buffer_flags) { + ctx->flags |= *buffer_flags; + *buffer_flags = 0; + } while (todo) { if (!ctx->buf) { if (list_empty(&ctx->dvb_q)) { @@ -395,6 +398,7 @@ int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b) int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b) { + unsigned long flags; int ret; ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking); @@ -402,7 +406,16 @@ int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b) dprintk(1, "[%s] errno=%d\n", ctx->name, ret); return ret; } - dprintk(5, "[%s] index=%d\n", ctx->name, b->index); + + spin_lock_irqsave(&ctx->slock, flags); + b->count = ctx->count++; + b->flags = ctx->flags; + ctx->flags = 0; + spin_unlock_irqrestore(&ctx->slock, flags); + + dprintk(5, "[%s] index=%d, count=%d, flags=%d\n", + ctx->name, b->index, ctx->count, b->flags); + return 0; } diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 60e9c2ba26be..787fe06df217 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -24,6 +24,7 @@ #include <linux/string.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/i2c.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> @@ -941,6 +942,55 @@ out: return err; } +#if IS_ENABLED(CONFIG_I2C) +struct i2c_client *dvb_module_probe(const char *module_name, + const char *name, + struct i2c_adapter *adap, + unsigned char addr, + void *platform_data) +{ + struct i2c_client *client; + struct i2c_board_info *board_info; + + board_info = kzalloc(sizeof(*board_info), GFP_KERNEL); + if (!board_info) + return NULL; + + if (name) + strlcpy(board_info->type, name, I2C_NAME_SIZE); + else + strlcpy(board_info->type, module_name, I2C_NAME_SIZE); + + board_info->addr = addr; + board_info->platform_data = platform_data; + request_module(module_name); + client = i2c_new_device(adap, board_info); + if (client == NULL || client->dev.driver == NULL) { + kfree(board_info); + return NULL; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + client = NULL; + } + + kfree(board_info); + return client; +} +EXPORT_SYMBOL_GPL(dvb_module_probe); + +void dvb_module_release(struct i2c_client *client) +{ + if (!client) + return; + + module_put(client->dev.driver->owner); + i2c_unregister_device(client); +} +EXPORT_SYMBOL_GPL(dvb_module_release); +#endif + static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) { struct dvb_device *dvbdev = dev_get_drvdata(dev); diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index d17722eb4456..0712069fd9fe 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -462,7 +462,7 @@ config DVB_TDA10048 config DVB_AF9013 tristate "Afatech AF9013 demodulator" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && I2C_MUX select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -546,6 +546,8 @@ config DVB_GP8PSK_FE depends on DVB_CORE default DVB_USB_GP8PSK +source "drivers/media/dvb-frontends/cxd2880/Kconfig" + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -822,13 +824,6 @@ config DVB_A8293 depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT -config DVB_SP2 - tristate "CIMaX SP2" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - CIMaX SP2/SP2HF Common Interface module. - config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C @@ -904,6 +899,27 @@ config DVB_HELENE help Say Y when you want to support this frontend. +comment "Common Interface (EN50221) controller drivers" + depends on DVB_CORE + +config DVB_CXD2099 + tristate "CXD2099AR Common Interface driver" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the CI controller currently found mostly on + Digital Devices DuoFlex CI (single) addon modules. + + Say Y when you want to support these devices. + +config DVB_SP2 + tristate "CIMaX SP2" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + CIMaX SP2/SP2HF Common Interface module. + 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 4be59fed4536..67a783fd5ed0 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -129,3 +129,5 @@ obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o obj-$(CONFIG_DVB_HELENE) += helene.o obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o +obj-$(CONFIG_DVB_CXD2880) += cxd2880/ diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index b8f3ebfc3e27..482bce49819a 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -23,6 +23,7 @@ struct af9013_state { struct i2c_client *client; struct regmap *regmap; + struct i2c_mux_core *muxc; struct dvb_frontend fe; u32 clk; u8 tuner; @@ -33,20 +34,20 @@ struct af9013_state { u8 api_version[4]; u8 gpio[4]; - /* tuner/demod RF and IF AGC limits used for signal strength calc */ - u8 signal_strength_en, rf_50, rf_80, if_50, if_80; - u16 signal_strength; - u32 ber; - u32 ucblocks; - u16 snr; u32 bandwidth_hz; enum fe_status fe_status; + /* RF and IF AGC limits used for signal strength calc */ + u8 strength_en, rf_agc_50, rf_agc_80, if_agc_50, if_agc_80; unsigned long set_frontend_jiffies; unsigned long read_status_jiffies; + unsigned long strength_jiffies; + unsigned long cnr_jiffies; + unsigned long ber_ucb_jiffies; + u16 dvbv3_snr; + u16 dvbv3_strength; + u32 dvbv3_ber; + u32 dvbv3_ucblocks; bool first_tune; - bool i2c_gate_state; - unsigned int statistics_step:3; - struct delayed_work statistics_work; }; static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) @@ -101,232 +102,6 @@ err: return ret; } -static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - int ret; - - dev_dbg(&client->dev, "\n"); - - /* reset and start BER counter */ - ret = regmap_update_bits(state->regmap, 0xd391, 0x10, 0x10); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - int ret; - unsigned int utmp; - u8 buf[5]; - - dev_dbg(&client->dev, "\n"); - - /* check if error bit count is ready */ - ret = regmap_read(state->regmap, 0xd391, &utmp); - if (ret) - goto err; - - if (!((utmp >> 4) & 0x01)) { - dev_dbg(&client->dev, "not ready\n"); - return 0; - } - - ret = regmap_bulk_read(state->regmap, 0xd387, buf, 5); - if (ret) - goto err; - - state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0]; - state->ucblocks += (buf[4] << 8) | buf[3]; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static int af9013_statistics_snr_start(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - int ret; - - dev_dbg(&client->dev, "\n"); - - /* start SNR meas */ - ret = regmap_update_bits(state->regmap, 0xd2e1, 0x08, 0x08); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static int af9013_statistics_snr_result(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i, len; - unsigned int utmp; - u8 buf[3]; - u32 snr_val; - const struct af9013_snr *uninitialized_var(snr_lut); - - dev_dbg(&client->dev, "\n"); - - /* check if SNR ready */ - ret = regmap_read(state->regmap, 0xd2e1, &utmp); - if (ret) - goto err; - - if (!((utmp >> 3) & 0x01)) { - dev_dbg(&client->dev, "not ready\n"); - return 0; - } - - /* read value */ - ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3); - if (ret) - goto err; - - snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; - - /* read current modulation */ - ret = regmap_read(state->regmap, 0xd3c1, &utmp); - if (ret) - goto err; - - switch ((utmp >> 6) & 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; - } - - for (i = 0; i < len; i++) { - utmp = snr_lut[i].snr; - - if (snr_val < snr_lut[i].val) - break; - } - state->snr = utmp * 10; /* dB/10 */ - - c->cnr.stat[0].svalue = 1000 * utmp; - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static int af9013_statistics_signal_strength(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - int ret = 0; - u8 buf[2], rf_gain, if_gain; - int signal_strength; - - dev_dbg(&client->dev, "\n"); - - if (!state->signal_strength_en) - return 0; - - ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2); - if (ret) - goto err; - - rf_gain = buf[0]; - if_gain = buf[1]; - - signal_strength = (0xffff / \ - (9 * (state->rf_50 + state->if_50) - \ - 11 * (state->rf_80 + state->if_80))) * \ - (10 * (rf_gain + if_gain) - \ - 11 * (state->rf_80 + state->if_80)); - if (signal_strength < 0) - signal_strength = 0; - else if (signal_strength > 0xffff) - signal_strength = 0xffff; - - state->signal_strength = signal_strength; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static void af9013_statistics_work(struct work_struct *work) -{ - struct af9013_state *state = container_of(work, - struct af9013_state, statistics_work.work); - unsigned int next_msec; - - /* update only signal strength when demod is not locked */ - if (!(state->fe_status & FE_HAS_LOCK)) { - state->statistics_step = 0; - state->ber = 0; - state->snr = 0; - } - - switch (state->statistics_step) { - default: - state->statistics_step = 0; - /* fall-through */ - case 0: - af9013_statistics_signal_strength(&state->fe); - state->statistics_step++; - next_msec = 300; - break; - case 1: - af9013_statistics_snr_start(&state->fe); - state->statistics_step++; - next_msec = 200; - break; - case 2: - af9013_statistics_ber_unc_start(&state->fe); - state->statistics_step++; - next_msec = 1000; - break; - case 3: - af9013_statistics_snr_result(&state->fe); - state->statistics_step++; - next_msec = 400; - break; - case 4: - af9013_statistics_ber_unc_result(&state->fe); - state->statistics_step++; - next_msec = 100; - break; - } - - schedule_delayed_work(&state->statistics_work, - msecs_to_jiffies(next_msec)); -} - static int af9013_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *fesettings) { @@ -751,46 +526,273 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct af9013_state *state = fe->demodulator_priv; struct i2c_client *client = state->client; - int ret; - unsigned int utmp; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, stmp1; + unsigned int utmp, utmp1, utmp2, utmp3, utmp4; + u8 buf[7]; + + dev_dbg(&client->dev, "\n"); /* * Return status from the cache if it is younger than 2000ms with the * exception of last tune is done during 4000ms. */ - if (time_is_after_jiffies( - state->read_status_jiffies + msecs_to_jiffies(2000)) && - time_is_before_jiffies( - state->set_frontend_jiffies + msecs_to_jiffies(4000)) - ) { - *status = state->fe_status; - return 0; + if (time_is_after_jiffies(state->read_status_jiffies + msecs_to_jiffies(2000)) && + time_is_before_jiffies(state->set_frontend_jiffies + msecs_to_jiffies(4000))) { + *status = state->fe_status; } else { - *status = 0; + /* MPEG2 lock */ + ret = regmap_read(state->regmap, 0xd507, &utmp); + if (ret) + goto err; + + if ((utmp >> 6) & 0x01) { + utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else { + /* TPS lock */ + ret = regmap_read(state->regmap, 0xd330, &utmp); + if (ret) + goto err; + + if ((utmp >> 3) & 0x01) + utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else + utmp1 = 0; + } + + dev_dbg(&client->dev, "fe_status %02x\n", utmp1); + + state->read_status_jiffies = jiffies; + + state->fe_status = utmp1; + *status = utmp1; } - /* MPEG2 lock */ - ret = regmap_read(state->regmap, 0xd507, &utmp); - if (ret) - goto err; + /* Signal strength */ + switch (state->strength_en) { + case 0: + /* Check if we support signal strength */ + ret = regmap_read(state->regmap, 0x9bee, &utmp); + if (ret) + goto err; - if ((utmp >> 6) & 0x01) - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_LOCK; + if ((utmp >> 0) & 0x01) { + /* Read agc values for signal strength estimation */ + ret = regmap_read(state->regmap, 0x9bbd, &utmp1); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9bd0, &utmp2); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9be2, &utmp3); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9be4, &utmp4); + if (ret) + goto err; - if (!*status) { - /* TPS lock */ - ret = regmap_read(state->regmap, 0xd330, &utmp); + state->rf_agc_50 = utmp1; + state->rf_agc_80 = utmp2; + state->if_agc_50 = utmp3; + state->if_agc_80 = utmp4; + dev_dbg(&client->dev, + "rf_agc_50 %u, rf_agc_80 %u, if_agc_50 %u, if_agc_80 %u\n", + utmp1, utmp2, utmp3, utmp4); + + state->strength_en = 1; + } else { + /* Signal strength is not supported */ + state->strength_en = 2; + break; + } + /* Fall through */ + case 1: + if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000))) + break; + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2); if (ret) goto err; - if ((utmp >> 3) & 0x01) - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI; + /* + * Construct line equation from tuner dependent -80/-50 dBm agc + * limits and use it to map current agc value to dBm estimate + */ + #define agc_gain (buf[0] + buf[1]) + #define agc_gain_50dbm (state->rf_agc_50 + state->if_agc_50) + #define agc_gain_80dbm (state->rf_agc_80 + state->if_agc_80) + stmp1 = 30000 * (agc_gain - agc_gain_80dbm) / + (agc_gain_50dbm - agc_gain_80dbm) - 80000; + + dev_dbg(&client->dev, + "strength %d, agc_gain %d, agc_gain_50dbm %d, agc_gain_80dbm %d\n", + stmp1, agc_gain, agc_gain_50dbm, agc_gain_80dbm); + + state->strength_jiffies = jiffies; + /* Convert [-90, -30] dBm to [0x0000, 0xffff] for dvbv3 */ + utmp1 = clamp(stmp1 + 90000, 0, 60000); + state->dvbv3_strength = div_u64((u64)utmp1 * 0xffff, 60000); + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = stmp1; + break; + default: + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; } - state->fe_status = *status; - state->read_status_jiffies = jiffies; + /* CNR */ + switch (state->fe_status & FE_HAS_VITERBI) { + case FE_HAS_VITERBI: + if (time_is_after_jiffies(state->cnr_jiffies + msecs_to_jiffies(2000))) + break; + + /* Check if cnr ready */ + ret = regmap_read(state->regmap, 0xd2e1, &utmp); + if (ret) + goto err; + + if (!((utmp >> 3) & 0x01)) { + dev_dbg(&client->dev, "cnr not ready\n"); + break; + } + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3); + if (ret) + goto err; + + utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + + /* Read current modulation */ + ret = regmap_read(state->regmap, 0xd3c1, &utmp); + if (ret) + goto err; + + switch ((utmp >> 6) & 3) { + case 0: + /* + * QPSK + * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6 + * value [653799, 1689999], 2.6 / 13 = 3355443 + */ + utmp1 = clamp(utmp1, 653799U, 1689999U); + utmp1 = ((u64)(intlog10(utmp1) + - intlog10(1690000 - utmp1) + + 3355443) * 13 * 1000) >> 24; + break; + case 1: + /* + * QAM-16 + * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7 + * value [371105, 827999], 15.7 / 6 = 43900382 + */ + utmp1 = clamp(utmp1, 371105U, 827999U); + utmp1 = ((u64)(intlog10(utmp1 - 370000) + - intlog10(828000 - utmp1) + + 43900382) * 6 * 1000) >> 24; + break; + case 2: + /* + * QAM-64 + * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8 + * value [193246, 424999], 23.8 / 8 = 49912218 + */ + utmp1 = clamp(utmp1, 193246U, 424999U); + utmp1 = ((u64)(intlog10(utmp1 - 193000) + - intlog10(425000 - utmp1) + + 49912218) * 8 * 1000) >> 24; + break; + default: + dev_dbg(&client->dev, "invalid modulation %u\n", + (utmp >> 6) & 3); + utmp1 = 0; + break; + } + + dev_dbg(&client->dev, "cnr %u\n", utmp1); + + state->cnr_jiffies = jiffies; + state->dvbv3_snr = utmp1 / 100; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = utmp1; + break; + default: + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } + + /* BER / PER */ + switch (state->fe_status & FE_HAS_SYNC) { + case FE_HAS_SYNC: + if (time_is_after_jiffies(state->ber_ucb_jiffies + msecs_to_jiffies(2000))) + break; + + /* Check if ber / ucb is ready */ + ret = regmap_read(state->regmap, 0xd391, &utmp); + if (ret) + goto err; + + if (!((utmp >> 4) & 0x01)) { + dev_dbg(&client->dev, "ber not ready\n"); + break; + } + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd385, buf, 7); + if (ret) + goto err; + + utmp1 = buf[4] << 16 | buf[3] << 8 | buf[2] << 0; + utmp2 = (buf[1] << 8 | buf[0] << 0) * 204 * 8; + utmp3 = buf[6] << 8 | buf[5] << 0; + utmp4 = buf[1] << 8 | buf[0] << 0; + + /* Use 10000 TS packets for measure */ + if (utmp4 != 10000) { + buf[0] = (10000 >> 0) & 0xff; + buf[1] = (10000 >> 8) & 0xff; + ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2); + if (ret) + goto err; + } + + /* Reset ber / ucb counter */ + ret = regmap_update_bits(state->regmap, 0xd391, 0x20, 0x20); + if (ret) + goto err; + + dev_dbg(&client->dev, "post_bit_error %u, post_bit_count %u\n", + utmp1, utmp2); + dev_dbg(&client->dev, "block_error %u, block_count %u\n", + utmp3, utmp4); + + state->ber_ucb_jiffies = jiffies; + state->dvbv3_ber = utmp1; + state->dvbv3_ucblocks += utmp3; + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += utmp1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp2; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += utmp3; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += utmp4; + break; + default: + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } return 0; err: @@ -801,28 +803,36 @@ err: static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) { struct af9013_state *state = fe->demodulator_priv; - *snr = state->snr; + + *snr = state->dvbv3_snr; + return 0; } static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct af9013_state *state = fe->demodulator_priv; - *strength = state->signal_strength; + + *strength = state->dvbv3_strength; + return 0; } static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) { struct af9013_state *state = fe->demodulator_priv; - *ber = state->ber; + + *ber = state->dvbv3_ber; + return 0; } static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct af9013_state *state = fe->demodulator_priv; - *ucblocks = state->ucblocks; + + *ucblocks = state->dvbv3_ucblocks; + return 0; } @@ -833,7 +843,7 @@ static int af9013_init(struct dvb_frontend *fe) int ret, i, len; unsigned int utmp; u8 buf[3]; - const struct af9013_reg_bit *init; + const struct af9013_reg_mask_val *tab; dev_dbg(&client->dev, "\n"); @@ -888,72 +898,66 @@ static int af9013_init(struct dvb_frontend *fe) if (ret) goto err; - /* load OFSM settings */ - dev_dbg(&client->dev, "load ofsm settings\n"); - len = ARRAY_SIZE(ofsm_init); - init = ofsm_init; + /* Demod core settings */ + dev_dbg(&client->dev, "load demod core settings\n"); + len = ARRAY_SIZE(demod_init_tab); + tab = demod_init_tab; for (i = 0; i < len; i++) { - u16 reg = init[i].addr; - u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos); - u8 val = init[i].val << init[i].pos; - - ret = regmap_update_bits(state->regmap, reg, mask, val); + ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask, + tab[i].val); if (ret) goto err; } - /* load tuner specific settings */ + /* Demod tuner specific settings */ dev_dbg(&client->dev, "load tuner specific settings\n"); switch (state->tuner) { case AF9013_TUNER_MXL5003D: - len = ARRAY_SIZE(tuner_init_mxl5003d); - init = tuner_init_mxl5003d; + len = ARRAY_SIZE(tuner_init_tab_mxl5003d); + tab = tuner_init_tab_mxl5003d; break; case AF9013_TUNER_MXL5005D: case AF9013_TUNER_MXL5005R: case AF9013_TUNER_MXL5007T: - len = ARRAY_SIZE(tuner_init_mxl5005); - init = tuner_init_mxl5005; + len = ARRAY_SIZE(tuner_init_tab_mxl5005); + tab = tuner_init_tab_mxl5005; break; case AF9013_TUNER_ENV77H11D5: - len = ARRAY_SIZE(tuner_init_env77h11d5); - init = tuner_init_env77h11d5; + len = ARRAY_SIZE(tuner_init_tab_env77h11d5); + tab = tuner_init_tab_env77h11d5; break; case AF9013_TUNER_MT2060: - len = ARRAY_SIZE(tuner_init_mt2060); - init = tuner_init_mt2060; + len = ARRAY_SIZE(tuner_init_tab_mt2060); + tab = tuner_init_tab_mt2060; break; case AF9013_TUNER_MC44S803: - len = ARRAY_SIZE(tuner_init_mc44s803); - init = tuner_init_mc44s803; + len = ARRAY_SIZE(tuner_init_tab_mc44s803); + tab = tuner_init_tab_mc44s803; break; case AF9013_TUNER_QT1010: case AF9013_TUNER_QT1010A: - len = ARRAY_SIZE(tuner_init_qt1010); - init = tuner_init_qt1010; + len = ARRAY_SIZE(tuner_init_tab_qt1010); + tab = tuner_init_tab_qt1010; break; case AF9013_TUNER_MT2060_2: - len = ARRAY_SIZE(tuner_init_mt2060_2); - init = tuner_init_mt2060_2; + len = ARRAY_SIZE(tuner_init_tab_mt2060_2); + tab = tuner_init_tab_mt2060_2; break; case AF9013_TUNER_TDA18271: case AF9013_TUNER_TDA18218: - len = ARRAY_SIZE(tuner_init_tda18271); - init = tuner_init_tda18271; + len = ARRAY_SIZE(tuner_init_tab_tda18271); + tab = tuner_init_tab_tda18271; break; case AF9013_TUNER_UNKNOWN: default: - len = ARRAY_SIZE(tuner_init_unknown); - init = tuner_init_unknown; + len = ARRAY_SIZE(tuner_init_tab_unknown); + tab = tuner_init_tab_unknown; break; } for (i = 0; i < len; i++) { - u16 reg = init[i].addr; - u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos); - u8 val = init[i].val << init[i].pos; - - ret = regmap_update_bits(state->regmap, reg, mask, val); + ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask, + tab[i].val); if (ret) goto err; } @@ -972,50 +976,7 @@ static int af9013_init(struct dvb_frontend *fe) if (ret) goto err; - /* check if we support signal strength */ - if (!state->signal_strength_en) { - ret = regmap_read(state->regmap, 0x9bee, &utmp); - if (ret) - goto err; - - state->signal_strength_en = (utmp >> 0) & 0x01; - } - - /* read values needed for signal strength calculation */ - if (state->signal_strength_en && !state->rf_50) { - ret = regmap_bulk_read(state->regmap, 0x9bbd, &state->rf_50, 1); - if (ret) - goto err; - ret = regmap_bulk_read(state->regmap, 0x9bd0, &state->rf_80, 1); - if (ret) - goto err; - ret = regmap_bulk_read(state->regmap, 0x9be2, &state->if_50, 1); - if (ret) - goto err; - ret = regmap_bulk_read(state->regmap, 0x9be4, &state->if_80, 1); - if (ret) - goto err; - } - - /* SNR */ - ret = regmap_write(state->regmap, 0xd2e2, 0x01); - if (ret) - goto err; - - /* BER / UCB */ - buf[0] = (10000 >> 0) & 0xff; - buf[1] = (10000 >> 8) & 0xff; - ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2); - if (ret) - goto err; - - /* enable FEC monitor */ - ret = regmap_update_bits(state->regmap, 0xd392, 0x02, 0x02); - if (ret) - goto err; - state->first_tune = true; - schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400)); return 0; err: @@ -1032,9 +993,6 @@ static int af9013_sleep(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); - /* stop statistics polling */ - cancel_delayed_work_sync(&state->statistics_work); - /* disable lock led */ ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00); if (ret) @@ -1072,45 +1030,6 @@ err: return ret; } -static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - int ret; - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - - dev_dbg(&client->dev, "enable %d\n", enable); - - /* gate already open or close */ - if (state->i2c_gate_state == enable) - return 0; - - if (state->ts_mode == AF9013_TS_MODE_USB) - ret = regmap_update_bits(state->regmap, 0xd417, 0x08, - enable << 3); - else - ret = regmap_update_bits(state->regmap, 0xd607, 0x04, - enable << 2); - if (ret) - goto err; - - state->i2c_gate_state = enable; - - return 0; -err: - dev_dbg(&client->dev, "failed %d\n", ret); - return ret; -} - -static void af9013_release(struct dvb_frontend *fe) -{ - struct af9013_state *state = fe->demodulator_priv; - struct i2c_client *client = state->client; - - dev_dbg(&client->dev, "\n"); - - i2c_unregister_device(client); -} - static const struct dvb_frontend_ops af9013_ops; static int af9013_download_firmware(struct af9013_state *state) @@ -1213,40 +1132,6 @@ err: return ret; } -/* - * XXX: That is wrapper to af9013_probe() via driver core in order to provide - * proper I2C client for legacy media attach binding. - * New users must use I2C client binding directly! - */ -struct dvb_frontend *af9013_attach(const struct af9013_config *config, - struct i2c_adapter *i2c) -{ - struct i2c_client *client; - struct i2c_board_info board_info; - struct af9013_platform_data pdata; - - pdata.clk = config->clock; - pdata.tuner = config->tuner; - pdata.if_frequency = config->if_frequency; - pdata.ts_mode = config->ts_mode; - pdata.ts_output_pin = 7; - pdata.spec_inv = config->spec_inv; - memcpy(&pdata.api_version, config->api_version, sizeof(pdata.api_version)); - memcpy(&pdata.gpio, config->gpio, sizeof(pdata.gpio)); - pdata.attach_in_use = true; - - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "af9013", sizeof(board_info.type)); - board_info.addr = config->i2c_addr; - board_info.platform_data = &pdata; - client = i2c_new_device(i2c, &board_info); - if (!client || !client->dev.driver) - return NULL; - - return pdata.get_dvb_frontend(client); -} -EXPORT_SYMBOL(af9013_attach); - static const struct dvb_frontend_ops af9013_ops = { .delsys = { SYS_DVBT }, .info = { @@ -1272,8 +1157,6 @@ static const struct dvb_frontend_ops af9013_ops = { FE_CAN_MUTE_TS }, - .release = af9013_release, - .init = af9013_init, .sleep = af9013_sleep, @@ -1286,10 +1169,58 @@ static const struct dvb_frontend_ops af9013_ops = { .read_signal_strength = af9013_read_signal_strength, .read_ber = af9013_read_ber, .read_ucblocks = af9013_read_ucblocks, - - .i2c_gate_ctrl = af9013_i2c_gate_ctrl, }; +static int af9013_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "onoff %d\n", onoff); + + ret = regmap_update_bits(state->regmap, 0xd503, 0x01, onoff); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, + int onoff) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + u8 buf[2]; + + dev_dbg(&client->dev, "index %d, pid %04x, onoff %d\n", + index, pid, onoff); + + if (pid > 0x1fff) { + /* 0x2000 is kernel virtual pid for whole ts (all pids) */ + ret = 0; + goto err; + } + + buf[0] = (pid >> 0) & 0xff; + buf[1] = (pid >> 8) & 0xff; + ret = regmap_bulk_write(state->regmap, 0xd505, buf, 2); + if (ret) + goto err; + ret = regmap_write(state->regmap, 0xd504, onoff << 5 | index << 0); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client) { struct af9013_state *state = i2c_get_clientdata(client); @@ -1299,9 +1230,65 @@ static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client) return &state->fe; } +static struct i2c_adapter *af9013_get_i2c_adapter(struct i2c_client *client) +{ + struct af9013_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return state->muxc->adapter[0]; +} + +/* + * XXX: Hackish solution. We use virtual register, reg bit 16, to carry info + * about i2c adapter locking. Own locking is needed because i2c mux call has + * already locked i2c adapter. + */ +static int af9013_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct af9013_state *state = i2c_mux_priv(muxc); + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + if (state->ts_mode == AF9013_TS_MODE_USB) + ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x08); + else + ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x04); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct af9013_state *state = i2c_mux_priv(muxc); + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + if (state->ts_mode == AF9013_TS_MODE_USB) + ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x00); + else + ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x00); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + /* Own I2C access routines needed for regmap as chip uses extra command byte */ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg, - const u8 *val, int len) + const u8 *val, int len, u8 lock) { int ret; u8 buf[21]; @@ -1323,7 +1310,12 @@ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg, buf[1] = (reg >> 0) & 0xff; buf[2] = cmd; memcpy(&buf[3], val, len); - ret = i2c_transfer(client->adapter, msg, 1); + + if (lock) + i2c_lock_adapter(client->adapter); + ret = __i2c_transfer(client->adapter, msg, 1); + if (lock) + i2c_unlock_adapter(client->adapter); if (ret < 0) { goto err; } else if (ret != 1) { @@ -1338,7 +1330,7 @@ err: } static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg, - u8 *val, int len) + u8 *val, int len, u8 lock) { int ret; u8 buf[3]; @@ -1359,7 +1351,12 @@ static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg, buf[0] = (reg >> 8) & 0xff; buf[1] = (reg >> 0) & 0xff; buf[2] = cmd; - ret = i2c_transfer(client->adapter, msg, 2); + + if (lock) + i2c_lock_adapter(client->adapter); + ret = __i2c_transfer(client->adapter, msg, 2); + if (lock) + i2c_unlock_adapter(client->adapter); if (ret < 0) { goto err; } else if (ret != 2) { @@ -1379,25 +1376,27 @@ static int af9013_regmap_write(void *context, const void *data, size_t count) struct af9013_state *state = i2c_get_clientdata(client); int ret, i; u8 cmd; - u16 reg = ((u8 *)data)[0] << 8|((u8 *)data)[1] << 0; - u8 *val = &((u8 *)data)[2]; - const unsigned int len = count - 2; + u8 lock = !((u8 *)data)[0]; + u16 reg = ((u8 *)data)[1] << 8 | ((u8 *)data)[2] << 0; + u8 *val = &((u8 *)data)[3]; + const unsigned int len = count - 3; if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) { cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0; - ret = af9013_wregs(client, cmd, reg, val, len); + ret = af9013_wregs(client, cmd, reg, val, len, lock); if (ret) goto err; } else if (reg >= 0x5100 && reg < 0x8fff) { /* Firmware download */ cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0; - ret = af9013_wregs(client, cmd, reg, val, len); + ret = af9013_wregs(client, cmd, reg, val, len, lock); if (ret) goto err; } else { cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0; for (i = 0; i < len; i++) { - ret = af9013_wregs(client, cmd, reg + i, val + i, 1); + ret = af9013_wregs(client, cmd, reg + i, val + i, 1, + lock); if (ret) goto err; } @@ -1416,19 +1415,21 @@ static int af9013_regmap_read(void *context, const void *reg_buf, struct af9013_state *state = i2c_get_clientdata(client); int ret, i; u8 cmd; - u16 reg = ((u8 *)reg_buf)[0] << 8|((u8 *)reg_buf)[1] << 0; + u8 lock = !((u8 *)reg_buf)[0]; + u16 reg = ((u8 *)reg_buf)[1] << 8 | ((u8 *)reg_buf)[2] << 0; u8 *val = &((u8 *)val_buf)[0]; const unsigned int len = val_size; if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) { cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0; - ret = af9013_rregs(client, cmd, reg, val_buf, len); + ret = af9013_rregs(client, cmd, reg, val_buf, len, lock); if (ret) goto err; } else { cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0; for (i = 0; i < len; i++) { - ret = af9013_rregs(client, cmd, reg + i, val + i, 1); + ret = af9013_rregs(client, cmd, reg + i, val + i, 1, + lock); if (ret) goto err; } @@ -1453,8 +1454,9 @@ static int af9013_probe(struct i2c_client *client, .write = af9013_regmap_write, }; static const struct regmap_config regmap_config = { - .reg_bits = 16, - .val_bits = 8, + /* Actual reg is 16 bits, see i2c adapter lock */ + .reg_bits = 24, + .val_bits = 8, }; state = kzalloc(sizeof(*state), GFP_KERNEL); @@ -1463,6 +1465,8 @@ static int af9013_probe(struct i2c_client *client, goto err; } + dev_dbg(&client->dev, "\n"); + /* Setup the state */ state->client = client; i2c_set_clientdata(client, state); @@ -1474,52 +1478,70 @@ static int af9013_probe(struct i2c_client *client, state->spec_inv = pdata->spec_inv; memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version)); memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio)); - INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work); state->regmap = regmap_init(&client->dev, ®map_bus, client, ®map_config); if (IS_ERR(state->regmap)) { ret = PTR_ERR(state->regmap); goto err_kfree; } + /* Create mux i2c adapter */ + state->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + af9013_select, af9013_deselect); + if (!state->muxc) { + ret = -ENOMEM; + goto err_regmap_exit; + } + state->muxc->priv = state; + ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; /* Download firmware */ if (state->ts_mode != AF9013_TS_MODE_USB) { ret = af9013_download_firmware(state); if (ret) - goto err_regmap_exit; + goto err_i2c_mux_del_adapters; } /* Firmware version */ ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version, sizeof(firmware_version)); if (ret) - goto err_regmap_exit; + goto err_i2c_mux_del_adapters; /* Set GPIOs */ for (i = 0; i < sizeof(state->gpio); i++) { ret = af9013_set_gpio(state, i, state->gpio[i]); if (ret) - goto err_regmap_exit; + goto err_i2c_mux_del_adapters; } /* Create dvb frontend */ memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops)); - if (!pdata->attach_in_use) - state->fe.ops.release = NULL; state->fe.demodulator_priv = state; /* Setup callbacks */ pdata->get_dvb_frontend = af9013_get_dvb_frontend; + pdata->get_i2c_adapter = af9013_get_i2c_adapter; + pdata->pid_filter = af9013_pid_filter; + pdata->pid_filter_ctrl = af9013_pid_filter_ctrl; /* Init stats to indicate which stats are supported */ c = &state->fe.dtv_property_cache; + c->strength.len = 1; c->cnr.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; dev_info(&client->dev, "Afatech AF9013 successfully attached\n"); dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n", firmware_version[0], firmware_version[1], firmware_version[2], firmware_version[3]); return 0; +err_i2c_mux_del_adapters: + i2c_mux_del_adapters(state->muxc); err_regmap_exit: regmap_exit(state->regmap); err_kfree: @@ -1535,8 +1557,7 @@ static int af9013_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - /* Stop statistics polling */ - cancel_delayed_work_sync(&state->statistics_work); + i2c_mux_del_adapters(state->muxc); regmap_exit(state->regmap); diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h index a290722c04fd..165ae29ccac4 100644 --- a/drivers/media/dvb-frontends/af9013.h +++ b/drivers/media/dvb-frontends/af9013.h @@ -38,13 +38,9 @@ * @api_version: Firmware API version. * @gpio: GPIOs. * @get_dvb_frontend: Get DVB frontend callback. - * - * AF9013/5 GPIOs (mostly guessed): - * * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices - * * demod#1-gpio#1 - xtal setting (?) - * * demod#1-gpio#3 - tuner#1 - * * demod#2-gpio#0 - tuner#2 - * * demod#2-gpio#1 - xtal setting (?) + * @get_i2c_adapter: Get I2C adapter. + * @pid_filter_ctrl: Control PID filter. + * @pid_filter: Set PID to PID filter. */ struct af9013_platform_data { /* @@ -84,36 +80,18 @@ struct af9013_platform_data { u8 gpio[4]; struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); - -/* private: For legacy media attach wrapper. Do not set value. */ - bool attach_in_use; - u8 i2c_addr; - u32 clock; + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); }; -#define af9013_config af9013_platform_data -#define AF9013_TS_USB AF9013_TS_MODE_USB -#define AF9013_TS_PARALLEL AF9013_TS_MODE_PARALLEL -#define AF9013_TS_SERIAL AF9013_TS_MODE_SERIAL - -#if IS_REACHABLE(CONFIG_DVB_AF9013) -/** - * Attach an af9013 demod - * - * @config: pointer to &struct af9013_config with demod configuration. - * @i2c: i2c adapter to use. - * - * return: FE pointer on success, NULL on failure. +/* + * AF9013/5 GPIOs (mostly guessed) + * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices + * demod#1-gpio#1 - xtal setting (?) + * demod#1-gpio#3 - tuner#1 + * demod#2-gpio#0 - tuner#2 + * demod#2-gpio#1 - xtal setting (?) */ -extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, - struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend *af9013_attach( -const struct af9013_config *config, struct i2c_adapter *i2c) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif /* CONFIG_DVB_AF9013 */ #endif /* AF9013_H */ diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h index 688fc3472cf6..3e95de7dba51 100644 --- a/drivers/media/dvb-frontends/af9013_priv.h +++ b/drivers/media/dvb-frontends/af9013_priv.h @@ -22,25 +22,21 @@ #define AF9013_PRIV_H #include <media/dvb_frontend.h> +#include <media/dvb_math.h> #include "af9013.h" #include <linux/firmware.h> +#include <linux/i2c-mux.h> #include <linux/math64.h> #include <linux/regmap.h> #define AF9013_FIRMWARE "dvb-fe-af9013.fw" -struct af9013_reg_bit { - u16 addr; - u8 pos:4; - u8 len:4; +struct af9013_reg_mask_val { + u16 reg; + u8 mask; u8 val; }; -struct af9013_snr { - u32 val; - u8 snr; -}; - struct af9013_coeff { u32 clock; u32 bandwidth_hz; @@ -91,817 +87,775 @@ static const struct af9013_coeff coeff_lut[] = { 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } }, }; -/* QPSK SNR lookup table */ -static const struct af9013_snr qpsk_snr_lut[] = { - { 0x000000, 0 }, - { 0x0b4771, 0 }, - { 0x0c1aed, 1 }, - { 0x0d0d27, 2 }, - { 0x0e4d19, 3 }, - { 0x0e5da8, 4 }, - { 0x107097, 5 }, - { 0x116975, 6 }, - { 0x1252d9, 7 }, - { 0x131fa4, 8 }, - { 0x13d5e1, 9 }, - { 0x148e53, 10 }, - { 0x15358b, 11 }, - { 0x15dd29, 12 }, - { 0x168112, 13 }, - { 0x170b61, 14 }, - { 0xffffff, 15 }, -}; - -/* QAM16 SNR lookup table */ -static const struct af9013_snr qam16_snr_lut[] = { - { 0x000000, 0 }, - { 0x05eb62, 5 }, - { 0x05fecf, 6 }, - { 0x060b80, 7 }, - { 0x062501, 8 }, - { 0x064865, 9 }, - { 0x069604, 10 }, - { 0x06f356, 11 }, - { 0x07706a, 12 }, - { 0x0804d3, 13 }, - { 0x089d1a, 14 }, - { 0x093e3d, 15 }, - { 0x09e35d, 16 }, - { 0x0a7c3c, 17 }, - { 0x0afaf8, 18 }, - { 0x0b719d, 19 }, - { 0xffffff, 20 }, -}; - -/* QAM64 SNR lookup table */ -static const struct af9013_snr qam64_snr_lut[] = { - { 0x000000, 0 }, - { 0x03109b, 12 }, - { 0x0310d4, 13 }, - { 0x031920, 14 }, - { 0x0322d0, 15 }, - { 0x0339fc, 16 }, - { 0x0364a1, 17 }, - { 0x038bcc, 18 }, - { 0x03c7d3, 19 }, - { 0x0408cc, 20 }, - { 0x043bed, 21 }, - { 0x048061, 22 }, - { 0x04be95, 23 }, - { 0x04fa7d, 24 }, - { 0x052405, 25 }, - { 0x05570d, 26 }, - { 0xffffff, 27 }, -}; - -static const struct af9013_reg_bit ofsm_init[] = { - { 0xd73a, 0, 8, 0xa1 }, - { 0xd73b, 0, 8, 0x1f }, - { 0xd73c, 4, 4, 0x0a }, - { 0xd732, 3, 1, 0x00 }, - { 0xd731, 4, 2, 0x03 }, - { 0xd73d, 7, 1, 0x01 }, - { 0xd740, 0, 1, 0x00 }, - { 0xd740, 1, 1, 0x00 }, - { 0xd740, 2, 1, 0x00 }, - { 0xd740, 3, 1, 0x01 }, - { 0xd3c1, 4, 1, 0x01 }, - { 0x9124, 0, 8, 0x58 }, - { 0x9125, 0, 2, 0x02 }, - { 0xd3a2, 0, 8, 0x00 }, - { 0xd3a3, 0, 8, 0x04 }, - { 0xd305, 0, 8, 0x32 }, - { 0xd306, 0, 8, 0x10 }, - { 0xd304, 0, 8, 0x04 }, - { 0x9112, 0, 1, 0x01 }, - { 0x911d, 0, 1, 0x01 }, - { 0x911a, 0, 1, 0x01 }, - { 0x911b, 0, 1, 0x01 }, - { 0x9bce, 0, 4, 0x02 }, - { 0x9116, 0, 1, 0x01 }, - { 0x9122, 0, 8, 0xd0 }, - { 0xd2e0, 0, 8, 0xd0 }, - { 0xd2e9, 0, 4, 0x0d }, - { 0xd38c, 0, 8, 0xfc }, - { 0xd38d, 0, 8, 0x00 }, - { 0xd38e, 0, 8, 0x7e }, - { 0xd38f, 0, 8, 0x00 }, - { 0xd390, 0, 8, 0x2f }, - { 0xd145, 4, 1, 0x01 }, - { 0xd1a9, 4, 1, 0x01 }, - { 0xd158, 5, 3, 0x01 }, - { 0xd159, 0, 6, 0x06 }, - { 0xd167, 0, 8, 0x00 }, - { 0xd168, 0, 4, 0x07 }, - { 0xd1c3, 5, 3, 0x00 }, - { 0xd1c4, 0, 6, 0x00 }, - { 0xd1c5, 0, 7, 0x10 }, - { 0xd1c6, 0, 3, 0x02 }, - { 0xd080, 2, 5, 0x03 }, - { 0xd081, 4, 4, 0x09 }, - { 0xd098, 4, 4, 0x0f }, - { 0xd098, 0, 4, 0x03 }, - { 0xdbc0, 4, 1, 0x01 }, - { 0xdbc7, 0, 8, 0x08 }, - { 0xdbc8, 4, 4, 0x00 }, - { 0xdbc9, 0, 5, 0x01 }, - { 0xd280, 0, 8, 0xe0 }, - { 0xd281, 0, 8, 0xff }, - { 0xd282, 0, 8, 0xff }, - { 0xd283, 0, 8, 0xc3 }, - { 0xd284, 0, 8, 0xff }, - { 0xd285, 0, 4, 0x01 }, - { 0xd0f0, 0, 7, 0x1a }, - { 0xd0f1, 4, 1, 0x01 }, - { 0xd0f2, 0, 8, 0x0c }, - { 0xd101, 5, 3, 0x06 }, - { 0xd103, 0, 4, 0x08 }, - { 0xd0f8, 0, 7, 0x20 }, - { 0xd111, 5, 1, 0x00 }, - { 0xd111, 6, 1, 0x00 }, - { 0x910b, 0, 8, 0x0a }, - { 0x9115, 0, 8, 0x02 }, - { 0x910c, 0, 8, 0x02 }, - { 0x910d, 0, 8, 0x08 }, - { 0x910e, 0, 8, 0x0a }, - { 0x9bf6, 0, 8, 0x06 }, - { 0x9bf8, 0, 8, 0x02 }, - { 0x9bf7, 0, 8, 0x05 }, - { 0x9bf9, 0, 8, 0x0f }, - { 0x9bfc, 0, 8, 0x13 }, - { 0x9bd3, 0, 8, 0xff }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, +/* + * Afatech AF9013 demod init + */ +static const struct af9013_reg_mask_val demod_init_tab[] = { + {0xd73a, 0xff, 0xa1}, + {0xd73b, 0xff, 0x1f}, + {0xd73c, 0xf0, 0xa0}, + {0xd732, 0x08, 0x00}, + {0xd731, 0x30, 0x30}, + {0xd73d, 0x80, 0x80}, + {0xd740, 0x01, 0x00}, + {0xd740, 0x02, 0x00}, + {0xd740, 0x04, 0x00}, + {0xd740, 0x08, 0x08}, + {0xd3c1, 0x10, 0x10}, + {0x9124, 0xff, 0x58}, + {0x9125, 0x03, 0x02}, + {0xd3a2, 0xff, 0x00}, + {0xd3a3, 0xff, 0x04}, + {0xd305, 0xff, 0x32}, + {0xd306, 0xff, 0x10}, + {0xd304, 0xff, 0x04}, + {0x9112, 0x01, 0x01}, + {0x911d, 0x01, 0x01}, + {0x911a, 0x01, 0x01}, + {0x911b, 0x01, 0x01}, + {0x9bce, 0x0f, 0x02}, + {0x9116, 0x01, 0x01}, + {0x9122, 0xff, 0xd0}, + {0xd2e0, 0xff, 0xd0}, + {0xd2e9, 0x0f, 0x0d}, + {0xd38c, 0xff, 0xfc}, + {0xd38d, 0xff, 0x00}, + {0xd38e, 0xff, 0x7e}, + {0xd38f, 0xff, 0x00}, + {0xd390, 0xff, 0x2f}, + {0xd145, 0x10, 0x10}, + {0xd1a9, 0x10, 0x10}, + {0xd158, 0xe0, 0x20}, + {0xd159, 0x3f, 0x06}, + {0xd167, 0xff, 0x00}, + {0xd168, 0x0f, 0x07}, + {0xd1c3, 0xe0, 0x00}, + {0xd1c4, 0x3f, 0x00}, + {0xd1c5, 0x7f, 0x10}, + {0xd1c6, 0x07, 0x02}, + {0xd080, 0x7c, 0x0c}, + {0xd081, 0xf0, 0x90}, + {0xd098, 0xf0, 0xf0}, + {0xd098, 0x0f, 0x03}, + {0xdbc0, 0x10, 0x10}, + {0xdbc7, 0xff, 0x08}, + {0xdbc8, 0xf0, 0x00}, + {0xdbc9, 0x1f, 0x01}, + {0xd280, 0xff, 0xe0}, + {0xd281, 0xff, 0xff}, + {0xd282, 0xff, 0xff}, + {0xd283, 0xff, 0xc3}, + {0xd284, 0xff, 0xff}, + {0xd285, 0x0f, 0x01}, + {0xd0f0, 0x7f, 0x1a}, + {0xd0f1, 0x10, 0x10}, + {0xd0f2, 0xff, 0x0c}, + {0xd101, 0xe0, 0xc0}, + {0xd103, 0x0f, 0x08}, + {0xd0f8, 0x7f, 0x20}, + {0xd111, 0x20, 0x00}, + {0xd111, 0x40, 0x00}, + {0x910b, 0xff, 0x0a}, + {0x9115, 0xff, 0x02}, + {0x910c, 0xff, 0x02}, + {0x910d, 0xff, 0x08}, + {0x910e, 0xff, 0x0a}, + {0x9bf6, 0xff, 0x06}, + {0x9bf8, 0xff, 0x02}, + {0x9bf7, 0xff, 0x05}, + {0x9bf9, 0xff, 0x0f}, + {0x9bfc, 0xff, 0x13}, + {0x9bd3, 0xff, 0xff}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, }; -/* Panasonic ENV77H11D5 tuner init - AF9013_TUNER_ENV77H11D5 = 129 */ -static const struct af9013_reg_bit tuner_init_env77h11d5[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x03 }, - { 0x9bbe, 0, 8, 0x01 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x00 }, - { 0x9be3, 0, 8, 0x00 }, - { 0xd015, 0, 8, 0x50 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0xdf }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x44 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0xeb }, - { 0xd00d, 0, 2, 0x02 }, - { 0xd00a, 0, 8, 0xf4 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bba, 0, 8, 0xf9 }, - { 0x9bc3, 0, 8, 0xdf }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0xeb }, - { 0x9bc6, 0, 8, 0x02 }, - { 0x9bc9, 0, 8, 0x52 }, - { 0xd011, 0, 8, 0x3c }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0xf7 }, - { 0xd014, 0, 2, 0x02 }, - { 0xd040, 0, 8, 0x0b }, - { 0xd041, 0, 2, 0x02 }, - { 0xd042, 0, 8, 0x4d }, - { 0xd043, 0, 2, 0x00 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, +/* + * Panasonic ENV77H11D5 tuner init + * AF9013_TUNER_ENV77H11D5 0x81 + */ +static const struct af9013_reg_mask_val tuner_init_tab_env77h11d5[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x03}, + {0x9bbe, 0xff, 0x01}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0xd015, 0xff, 0x50}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0xeb}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf4}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0xeb}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0x52}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, }; -/* Microtune MT2060 tuner init - AF9013_TUNER_MT2060 = 130 */ -static const struct af9013_reg_bit tuner_init_mt2060[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x07 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x00 }, - { 0x9be3, 0, 8, 0x00 }, - { 0x9bbe, 0, 1, 0x00 }, - { 0x9bcc, 0, 1, 0x00 }, - { 0x9bb9, 0, 8, 0x75 }, - { 0x9bcd, 0, 8, 0x24 }, - { 0x9bff, 0, 8, 0x30 }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0x0f }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x32 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0x36 }, - { 0xd00d, 0, 2, 0x03 }, - { 0xd00a, 0, 8, 0x35 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x07 }, - { 0x9bc8, 0, 8, 0x90 }, - { 0x9bc3, 0, 8, 0x0f }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x36 }, - { 0x9bc6, 0, 8, 0x03 }, - { 0x9bba, 0, 8, 0xc9 }, - { 0x9bc9, 0, 8, 0x79 }, - { 0xd011, 0, 8, 0x10 }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0x45 }, - { 0xd014, 0, 2, 0x03 }, - { 0xd040, 0, 8, 0x98 }, - { 0xd041, 0, 2, 0x00 }, - { 0xd042, 0, 8, 0xcf }, - { 0xd043, 0, 2, 0x03 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, - { 0x9bd0, 0, 8, 0xcc }, - { 0x9be4, 0, 8, 0xa0 }, - { 0x9bbd, 0, 8, 0x8e }, - { 0x9be2, 0, 8, 0x4d }, - { 0x9bee, 0, 1, 0x01 }, +/* + * Microtune MT2060 tuner init + * AF9013_TUNER_MT2060 0x82 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mt2060[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x07}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bbe, 0x01, 0x00}, + {0x9bcc, 0x01, 0x00}, + {0x9bb9, 0xff, 0x75}, + {0x9bcd, 0xff, 0x24}, + {0x9bff, 0xff, 0x30}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x32}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x36}, + {0xd00d, 0x03, 0x03}, + {0xd00a, 0xff, 0x35}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x90}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x36}, + {0x9bc6, 0xff, 0x03}, + {0x9bba, 0xff, 0xc9}, + {0x9bc9, 0xff, 0x79}, + {0xd011, 0xff, 0x10}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0x45}, + {0xd014, 0x03, 0x03}, + {0xd040, 0xff, 0x98}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0xcf}, + {0xd043, 0x03, 0x03}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xcc}, + {0x9be4, 0xff, 0xa0}, + {0x9bbd, 0xff, 0x8e}, + {0x9be2, 0xff, 0x4d}, + {0x9bee, 0x01, 0x01}, }; -/* Microtune MT2060 tuner init - AF9013_TUNER_MT2060_2 = 147 */ -static const struct af9013_reg_bit tuner_init_mt2060_2[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x06 }, - { 0x9bbe, 0, 8, 0x01 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0x0f }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x32 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0x36 }, - { 0xd00d, 0, 2, 0x03 }, - { 0xd00a, 0, 8, 0x35 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x07 }, - { 0x9bc8, 0, 8, 0x90 }, - { 0x9bc3, 0, 8, 0x0f }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x36 }, - { 0x9bc6, 0, 8, 0x03 }, - { 0x9bba, 0, 8, 0xc9 }, - { 0x9bc9, 0, 8, 0x79 }, - { 0xd011, 0, 8, 0x10 }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0x45 }, - { 0xd014, 0, 2, 0x03 }, - { 0xd040, 0, 8, 0x98 }, - { 0xd041, 0, 2, 0x00 }, - { 0xd042, 0, 8, 0xcf }, - { 0xd043, 0, 2, 0x03 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 8, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x96 }, - { 0xd054, 0, 8, 0x46 }, - { 0xd045, 7, 1, 0x00 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, +/* + * Microtune MT2060 tuner init + * AF9013_TUNER_MT2060_2 0x93 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mt2060_2[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x06}, + {0x9bbe, 0xff, 0x01}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x32}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x36}, + {0xd00d, 0x03, 0x03}, + {0xd00a, 0xff, 0x35}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x90}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x36}, + {0x9bc6, 0xff, 0x03}, + {0x9bba, 0xff, 0xc9}, + {0x9bc9, 0xff, 0x79}, + {0xd011, 0xff, 0x10}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0x45}, + {0xd014, 0x03, 0x03}, + {0xd040, 0xff, 0x98}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0xcf}, + {0xd043, 0x03, 0x03}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0xff, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x96}, + {0xd054, 0xff, 0x46}, + {0xd045, 0x80, 0x00}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, }; -/* MaxLinear MXL5003 tuner init - AF9013_TUNER_MXL5003D = 3 */ -static const struct af9013_reg_bit tuner_init_mxl5003d[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x09 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x00 }, - { 0x9be3, 0, 8, 0x00 }, - { 0x9bfc, 0, 8, 0x0f }, - { 0x9bf6, 0, 8, 0x01 }, - { 0x9bbe, 0, 1, 0x01 }, - { 0xd015, 0, 8, 0x33 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x40 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0x0f }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x6c }, - { 0xd007, 0, 2, 0x00 }, - { 0xd00c, 0, 8, 0x3d }, - { 0xd00d, 0, 2, 0x00 }, - { 0xd00a, 0, 8, 0x45 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x07 }, - { 0x9bc8, 0, 8, 0x52 }, - { 0x9bc3, 0, 8, 0x0f }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x3d }, - { 0x9bc6, 0, 8, 0x00 }, - { 0x9bba, 0, 8, 0xa2 }, - { 0x9bc9, 0, 8, 0xa0 }, - { 0xd011, 0, 8, 0x56 }, - { 0xd012, 0, 2, 0x00 }, - { 0xd013, 0, 8, 0x50 }, - { 0xd014, 0, 2, 0x00 }, - { 0xd040, 0, 8, 0x56 }, - { 0xd041, 0, 2, 0x00 }, - { 0xd042, 0, 8, 0x50 }, - { 0xd043, 0, 2, 0x00 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 8, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, +/* + * MaxLinear MXL5003 tuner init + * AF9013_TUNER_MXL5003D 0x03 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mxl5003d[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x09}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bfc, 0xff, 0x0f}, + {0x9bf6, 0xff, 0x01}, + {0x9bbe, 0x01, 0x01}, + {0xd015, 0xff, 0x33}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x40}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x6c}, + {0xd007, 0x03, 0x00}, + {0xd00c, 0xff, 0x3d}, + {0xd00d, 0x03, 0x00}, + {0xd00a, 0xff, 0x45}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x52}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x3d}, + {0x9bc6, 0xff, 0x00}, + {0x9bba, 0xff, 0xa2}, + {0x9bc9, 0xff, 0xa0}, + {0xd011, 0xff, 0x56}, + {0xd012, 0x03, 0x00}, + {0xd013, 0xff, 0x50}, + {0xd014, 0x03, 0x00}, + {0xd040, 0xff, 0x56}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0x50}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0xff, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, }; -/* MaxLinear MXL5005S & MXL5007T tuner init - AF9013_TUNER_MXL5005D = 13 - AF9013_TUNER_MXL5005R = 30 - AF9013_TUNER_MXL5007T = 177 */ -static const struct af9013_reg_bit tuner_init_mxl5005[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x07 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x01 }, - { 0x9be3, 0, 8, 0x01 }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, - { 0x9bb9, 0, 8, 0x00 }, - { 0x9bcd, 0, 8, 0x28 }, - { 0x9bff, 0, 8, 0x24 }, - { 0xd015, 0, 8, 0x40 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x40 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0x0f }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x73 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0xfa }, - { 0xd00d, 0, 2, 0x01 }, - { 0xd00a, 0, 8, 0xff }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x23 }, - { 0x9bc8, 0, 8, 0x55 }, - { 0x9bc3, 0, 8, 0x01 }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0xfa }, - { 0x9bc6, 0, 8, 0x01 }, - { 0x9bba, 0, 8, 0xff }, - { 0x9bc9, 0, 8, 0xff }, - { 0x9bd3, 0, 8, 0x95 }, - { 0xd011, 0, 8, 0x70 }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0xfb }, - { 0xd014, 0, 2, 0x01 }, - { 0xd040, 0, 8, 0x70 }, - { 0xd041, 0, 2, 0x01 }, - { 0xd042, 0, 8, 0xfb }, - { 0xd043, 0, 2, 0x01 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, - { 0x9bd0, 0, 8, 0x93 }, - { 0x9be4, 0, 8, 0xfe }, - { 0x9bbd, 0, 8, 0x63 }, - { 0x9be2, 0, 8, 0xfe }, - { 0x9bee, 0, 1, 0x01 }, +/* + * MaxLinear MXL5005S & MXL5007T tuner init + * AF9013_TUNER_MXL5005D 0x0d + * AF9013_TUNER_MXL5005R 0x1e + * AF9013_TUNER_MXL5007T 0xb1 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mxl5005[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x07}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x28}, + {0x9bff, 0xff, 0x24}, + {0xd015, 0xff, 0x40}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x40}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x73}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0xfa}, + {0xd00d, 0x03, 0x01}, + {0xd00a, 0xff, 0xff}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x23}, + {0x9bc8, 0xff, 0x55}, + {0x9bc3, 0xff, 0x01}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0xfa}, + {0x9bc6, 0xff, 0x01}, + {0x9bba, 0xff, 0xff}, + {0x9bc9, 0xff, 0xff}, + {0x9bd3, 0xff, 0x95}, + {0xd011, 0xff, 0x70}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xfb}, + {0xd014, 0x03, 0x01}, + {0xd040, 0xff, 0x70}, + {0xd041, 0x03, 0x01}, + {0xd042, 0xff, 0xfb}, + {0xd043, 0x03, 0x01}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0x93}, + {0x9be4, 0xff, 0xfe}, + {0x9bbd, 0xff, 0x63}, + {0x9be2, 0xff, 0xfe}, + {0x9bee, 0x01, 0x01}, }; -/* Quantek QT1010 tuner init - AF9013_TUNER_QT1010 = 134 - AF9013_TUNER_QT1010A = 162 */ -static const struct af9013_reg_bit tuner_init_qt1010[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x09 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x01 }, - { 0x9be3, 0, 8, 0x01 }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, - { 0x9bb9, 0, 8, 0x00 }, - { 0x9bcd, 0, 8, 0x28 }, - { 0x9bff, 0, 8, 0x20 }, - { 0xd008, 0, 8, 0x0f }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x99 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0x0f }, - { 0xd00d, 0, 2, 0x02 }, - { 0xd00a, 0, 8, 0x50 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x00 }, - { 0x9bc8, 0, 8, 0x00 }, - { 0x9bc3, 0, 8, 0x0f }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x0f }, - { 0x9bc6, 0, 8, 0x02 }, - { 0x9bba, 0, 8, 0xc5 }, - { 0x9bc9, 0, 8, 0xff }, - { 0xd011, 0, 8, 0x58 }, - { 0xd012, 0, 2, 0x02 }, - { 0xd013, 0, 8, 0x89 }, - { 0xd014, 0, 2, 0x01 }, - { 0xd040, 0, 8, 0x58 }, - { 0xd041, 0, 2, 0x02 }, - { 0xd042, 0, 8, 0x89 }, - { 0xd043, 0, 2, 0x01 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, - { 0x9bd0, 0, 8, 0xcd }, - { 0x9be4, 0, 8, 0xbb }, - { 0x9bbd, 0, 8, 0x93 }, - { 0x9be2, 0, 8, 0x80 }, - { 0x9bee, 0, 1, 0x01 }, +/* + * Quantek QT1010 tuner init + * AF9013_TUNER_QT1010 0x86 + * AF9013_TUNER_QT1010A 0xa2 + */ +static const struct af9013_reg_mask_val tuner_init_tab_qt1010[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x09}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x28}, + {0x9bff, 0xff, 0x20}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x99}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x0f}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0x50}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x00}, + {0x9bc8, 0xff, 0x00}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x0f}, + {0x9bc6, 0xff, 0x02}, + {0x9bba, 0xff, 0xc5}, + {0x9bc9, 0xff, 0xff}, + {0xd011, 0xff, 0x58}, + {0xd012, 0x03, 0x02}, + {0xd013, 0xff, 0x89}, + {0xd014, 0x03, 0x01}, + {0xd040, 0xff, 0x58}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x89}, + {0xd043, 0x03, 0x01}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xcd}, + {0x9be4, 0xff, 0xbb}, + {0x9bbd, 0xff, 0x93}, + {0x9be2, 0xff, 0x80}, + {0x9bee, 0x01, 0x01}, }; -/* Freescale MC44S803 tuner init - AF9013_TUNER_MC44S803 = 133 */ -static const struct af9013_reg_bit tuner_init_mc44s803[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x06 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x00 }, - { 0x9be3, 0, 8, 0x00 }, - { 0x9bf6, 0, 8, 0x01 }, - { 0x9bf8, 0, 8, 0x02 }, - { 0x9bf9, 0, 8, 0x02 }, - { 0x9bfc, 0, 8, 0x1f }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, - { 0x9bb9, 0, 8, 0x00 }, - { 0x9bcd, 0, 8, 0x24 }, - { 0x9bff, 0, 8, 0x24 }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0x01 }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x7b }, - { 0xd007, 0, 2, 0x00 }, - { 0xd00c, 0, 8, 0x7c }, - { 0xd00d, 0, 2, 0x02 }, - { 0xd00a, 0, 8, 0xfe }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bc7, 0, 8, 0x08 }, - { 0x9bc8, 0, 8, 0x9a }, - { 0x9bc3, 0, 8, 0x01 }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x7c }, - { 0x9bc6, 0, 8, 0x02 }, - { 0x9bba, 0, 8, 0xfc }, - { 0x9bc9, 0, 8, 0xaa }, - { 0xd011, 0, 8, 0x6b }, - { 0xd012, 0, 2, 0x00 }, - { 0xd013, 0, 8, 0x88 }, - { 0xd014, 0, 2, 0x02 }, - { 0xd040, 0, 8, 0x6b }, - { 0xd041, 0, 2, 0x00 }, - { 0xd042, 0, 8, 0x7c }, - { 0xd043, 0, 2, 0x02 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, - { 0x9bd0, 0, 8, 0x9e }, - { 0x9be4, 0, 8, 0xff }, - { 0x9bbd, 0, 8, 0x9e }, - { 0x9be2, 0, 8, 0x25 }, - { 0x9bee, 0, 1, 0x01 }, - { 0xd73b, 3, 1, 0x00 }, +/* + * Freescale MC44S803 tuner init + * AF9013_TUNER_MC44S803 0x85 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mc44s803[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x06}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bf6, 0xff, 0x01}, + {0x9bf8, 0xff, 0x02}, + {0x9bf9, 0xff, 0x02}, + {0x9bfc, 0xff, 0x1f}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x24}, + {0x9bff, 0xff, 0x24}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x01}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x7b}, + {0xd007, 0x03, 0x00}, + {0xd00c, 0xff, 0x7c}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xfe}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x08}, + {0x9bc8, 0xff, 0x9a}, + {0x9bc3, 0xff, 0x01}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x7c}, + {0x9bc6, 0xff, 0x02}, + {0x9bba, 0xff, 0xfc}, + {0x9bc9, 0xff, 0xaa}, + {0xd011, 0xff, 0x6b}, + {0xd012, 0x03, 0x00}, + {0xd013, 0xff, 0x88}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x6b}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0x7c}, + {0xd043, 0x03, 0x02}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0x9e}, + {0x9be4, 0xff, 0xff}, + {0x9bbd, 0xff, 0x9e}, + {0x9be2, 0xff, 0x25}, + {0x9bee, 0x01, 0x01}, + {0xd73b, 0x08, 0x00}, }; -/* unknown, probably for tin can tuner, tuner init - AF9013_TUNER_UNKNOWN = 140 */ -static const struct af9013_reg_bit tuner_init_unknown[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x02 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x01 }, - { 0x9be3, 0, 8, 0x01 }, - { 0xd1a0, 1, 1, 0x00 }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, - { 0x9bb9, 0, 8, 0x00 }, - { 0x9bcd, 0, 8, 0x18 }, - { 0x9bff, 0, 8, 0x2c }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0xdf }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x44 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0x00 }, - { 0xd00d, 0, 2, 0x02 }, - { 0xd00a, 0, 8, 0xf6 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bba, 0, 8, 0xf9 }, - { 0x9bc8, 0, 8, 0xaa }, - { 0x9bc3, 0, 8, 0xdf }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x00 }, - { 0x9bc6, 0, 8, 0x02 }, - { 0x9bc9, 0, 8, 0xf0 }, - { 0xd011, 0, 8, 0x3c }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0xf7 }, - { 0xd014, 0, 2, 0x02 }, - { 0xd040, 0, 8, 0x0b }, - { 0xd041, 0, 2, 0x02 }, - { 0xd042, 0, 8, 0x4d }, - { 0xd043, 0, 2, 0x00 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, +/* + * Unknown, probably for tin can tuner, tuner init + * AF9013_TUNER_UNKNOWN 0x8c + */ +static const struct af9013_reg_mask_val tuner_init_tab_unknown[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x02}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd1a0, 0x02, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x18}, + {0x9bff, 0xff, 0x2c}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x00}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf6}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc8, 0xff, 0xaa}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x00}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0xf0}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, }; -/* NXP TDA18271 & TDA18218 tuner init - AF9013_TUNER_TDA18271 = 156 - AF9013_TUNER_TDA18218 = 179 */ -static const struct af9013_reg_bit tuner_init_tda18271[] = { - { 0x9bd5, 0, 8, 0x01 }, - { 0x9bd6, 0, 8, 0x04 }, - { 0xd1a0, 1, 1, 0x01 }, - { 0xd000, 0, 1, 0x01 }, - { 0xd000, 1, 1, 0x00 }, - { 0xd001, 1, 1, 0x01 }, - { 0xd001, 0, 1, 0x00 }, - { 0xd001, 5, 1, 0x00 }, - { 0xd002, 0, 5, 0x19 }, - { 0xd003, 0, 5, 0x1a }, - { 0xd004, 0, 5, 0x19 }, - { 0xd005, 0, 5, 0x1a }, - { 0xd00e, 0, 5, 0x10 }, - { 0xd00f, 0, 3, 0x04 }, - { 0xd00f, 3, 3, 0x05 }, - { 0xd010, 0, 3, 0x04 }, - { 0xd010, 3, 3, 0x05 }, - { 0xd016, 4, 4, 0x03 }, - { 0xd01f, 0, 6, 0x0a }, - { 0xd020, 0, 6, 0x0a }, - { 0x9bda, 0, 8, 0x01 }, - { 0x9be3, 0, 8, 0x01 }, - { 0xd1a0, 1, 1, 0x00 }, - { 0x9bbe, 0, 1, 0x01 }, - { 0x9bcc, 0, 1, 0x01 }, - { 0x9bb9, 0, 8, 0x00 }, - { 0x9bcd, 0, 8, 0x18 }, - { 0x9bff, 0, 8, 0x2c }, - { 0xd015, 0, 8, 0x46 }, - { 0xd016, 0, 1, 0x00 }, - { 0xd044, 0, 8, 0x46 }, - { 0xd045, 0, 1, 0x00 }, - { 0xd008, 0, 8, 0xdf }, - { 0xd009, 0, 2, 0x02 }, - { 0xd006, 0, 8, 0x44 }, - { 0xd007, 0, 2, 0x01 }, - { 0xd00c, 0, 8, 0x00 }, - { 0xd00d, 0, 2, 0x02 }, - { 0xd00a, 0, 8, 0xf6 }, - { 0xd00b, 0, 2, 0x01 }, - { 0x9bba, 0, 8, 0xf9 }, - { 0x9bc8, 0, 8, 0xaa }, - { 0x9bc3, 0, 8, 0xdf }, - { 0x9bc4, 0, 8, 0x02 }, - { 0x9bc5, 0, 8, 0x00 }, - { 0x9bc6, 0, 8, 0x02 }, - { 0x9bc9, 0, 8, 0xf0 }, - { 0xd011, 0, 8, 0x3c }, - { 0xd012, 0, 2, 0x01 }, - { 0xd013, 0, 8, 0xf7 }, - { 0xd014, 0, 2, 0x02 }, - { 0xd040, 0, 8, 0x0b }, - { 0xd041, 0, 2, 0x02 }, - { 0xd042, 0, 8, 0x4d }, - { 0xd043, 0, 2, 0x00 }, - { 0xd045, 1, 1, 0x00 }, - { 0x9bcf, 0, 1, 0x01 }, - { 0xd045, 2, 1, 0x01 }, - { 0xd04f, 0, 8, 0x9a }, - { 0xd050, 0, 1, 0x01 }, - { 0xd051, 0, 8, 0x5a }, - { 0xd052, 0, 1, 0x01 }, - { 0xd053, 0, 8, 0x50 }, - { 0xd054, 0, 8, 0x46 }, - { 0x9bd7, 0, 8, 0x0a }, - { 0x9bd8, 0, 8, 0x14 }, - { 0x9bd9, 0, 8, 0x08 }, - { 0x9bd0, 0, 8, 0xa8 }, - { 0x9be4, 0, 8, 0x7f }, - { 0x9bbd, 0, 8, 0xa8 }, - { 0x9be2, 0, 8, 0x20 }, - { 0x9bee, 0, 1, 0x01 }, +/* + * NXP TDA18271 & TDA18218 tuner init + * AF9013_TUNER_TDA18271 0x9c + * AF9013_TUNER_TDA18218 0xb3 + */ +static const struct af9013_reg_mask_val tuner_init_tab_tda18271[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x04}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd1a0, 0x02, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x18}, + {0x9bff, 0xff, 0x2c}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x00}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf6}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc8, 0xff, 0xaa}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x00}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0xf0}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xa8}, + {0x9be4, 0xff, 0x7f}, + {0x9bbd, 0xff, 0xa8}, + {0x9be2, 0xff, 0x20}, + {0x9bee, 0x01, 0x01}, }; #endif /* AF9013_PRIV_H */ diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c new file mode 100644 index 000000000000..c0a5849b76bb --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -0,0 +1,704 @@ +/* + * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010-2013 Digital Devices GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include "cxd2099.h" + +static int buffermode; +module_param(buffermode, int, 0444); +MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)"); + +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount); + +struct cxd { + struct dvb_ca_en50221 en; + + struct cxd2099_cfg cfg; + struct i2c_client *client; + struct regmap *regmap; + + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + int ready; + int dr; + int write_busy; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; /* device access lock */ + + u8 rbuf[1028]; + u8 wbuf[1028]; +}; + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n) +{ + int status = 0; + + if (ci->lastaddress != adr) + status = regmap_write(ci->regmap, 0, adr); + if (!status) { + ci->lastaddress = adr; + + while (n) { + int len = n; + + if (ci->cfg.max_i2c && len > ci->cfg.max_i2c) + len = ci->cfg.max_i2c; + status = regmap_raw_read(ci->regmap, 1, data, len); + if (status) + return status; + data += len; + n -= len; + } + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_raw_read(ci->regmap, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) { + u8 buf[256]; + + memcpy(buf, data, n); + status = regmap_raw_write(ci->regmap, 3, buf, n); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, unsigned int *val) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_read(ci->regmap, 3, val); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_write(ci->regmap, 3, val); + return status; +} + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status = 0; + unsigned int regval; + + if (ci->lastaddress != reg) + status = regmap_write(ci->regmap, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) { + status = regmap_read(ci->regmap, 1, ®val); + ci->regs[reg] = regval; + } + ci->lastaddress = reg; + ci->regs[reg] = (ci->regs[reg] & (~mask)) | val; + if (!status) + status = regmap_write(ci->regmap, 1, ci->regs[reg]); + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n) +{ + int status = 0; + u8 *buf = ci->wbuf; + + if (ci->lastaddress != adr) + status = regmap_write(ci->regmap, 0, adr); + if (status) + return status; + + ci->lastaddress = adr; + while (n) { + int len = n; + + if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c)) + len = ci->cfg.max_i2c - 1; + memcpy(buf, data, len); + status = regmap_raw_write(ci->regmap, 1, buf, len); + if (status) + return status; + n -= len; + data += len; + } + return status; +} + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + u8 dummy; + + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: + if (!ci->en.read_data) + return; + ci->write_busy = 0; + dev_info(&ci->client->dev, "enable cam buffer mode\n"); + write_reg(ci, 0x0d, 0x00); + write_reg(ci, 0x0e, 0x01); + write_regm(ci, 0x08, 0x40, 0x40); + read_reg(ci, 0x12, &dummy); + write_regm(ci, 0x08, 0x80, 0x80); + break; + default: + break; + } + ci->cammode = mode; +} + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + status = write_reg(ci, 0x00, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x02, 0x10); + if (status < 0) + break; + status = write_reg(ci, 0x03, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x05, 0xFF); + if (status < 0) + break; + status = write_reg(ci, 0x06, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x07, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x08, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x14, 0x20); + if (status < 0) + break; + + /* TOSTRT = 8, Mode B (gated clock), falling Edge, + * Serial, POL=HIGH, MSB + */ + status = write_reg(ci, 0x0A, 0xA7); + if (status < 0) + break; + + status = write_reg(ci, 0x0B, 0x33); + if (status < 0) + break; + status = write_reg(ci, 0x0C, 0x33); + if (status < 0) + break; + + status = write_regm(ci, 0x14, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x15, ci->clk_reg_b); + if (status < 0) + break; + status = write_regm(ci, 0x16, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x17, ci->clk_reg_f); + if (status < 0) + break; + + if (ci->cfg.clock_mode == 2) { + /* bitrate*2^13/ 72000 */ + u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000; + + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x08); + if (status < 0) + break; + status = write_reg(ci, 0x21, (reg >> 8) & 0xff); + if (status < 0) + break; + status = write_reg(ci, 0x22, reg & 0xff); + if (status < 0) + break; + } else if (ci->cfg.clock_mode == 1) { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); /* D */ + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x68); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x02); + if (status < 0) + break; + } else { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x4f); /* C */ + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x4d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x07); + if (status < 0) + break; + } + + status = write_regm(ci, 0x20, 0x80, 0x80); + if (status < 0) + break; + status = write_regm(ci, 0x03, 0x02, 0x02); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x04); + if (status < 0) + break; + status = write_reg(ci, 0x00, 0x31); + if (status < 0) + break; + + /* Put TS in bypass */ + status = write_regm(ci, 0x09, 0x08, 0x08); + if (status < 0) + break; + ci->cammode = -1; + cam_mode(ci, 0); + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + return val; +} + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + unsigned int val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); + + mutex_lock(&ci->lock); + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; + ci->mode = -1; + { + int i; + + for (i = 0; i < 100; i++) { + usleep_range(10000, 11000); + if (ci->ready) + break; + } + } + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + dev_dbg(&ci->client->dev, "%s\n", __func__); + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); + mutex_lock(&ci->lock); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + msleep(300); + + write_regm(ci, 0x09, 0x08, 0x08); + write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ + write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */ + + ci->mode = -1; + ci->write_busy = 0; + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); + cam_mode(ci, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat & 0x40) + ci->dr = 1; + if (istat & 0x20) + ci->write_busy = 0; + + if (istat & 2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2 & slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= + DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + dev_info(&ci->client->dev, "NO CAM\n"); + ci->ready = 0; + } + } + if ((istat & 8) && + ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + } + } + return 0; +} + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = ((u16)msb << 8) | lsb; + if (len > ecount || len < 2) { + /* read it anyway or cxd may hang */ + read_block(ci, 0x12, ci->rbuf, len); + mutex_unlock(&ci->lock); + return -EIO; + } + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + if (ci->write_busy) + return -EAGAIN; + mutex_lock(&ci->lock); + write_reg(ci, 0x0d, ecount >> 8); + write_reg(ci, 0x0e, ecount & 0xff); + write_block(ci, 0x11, ebuf, ecount); + ci->write_busy = 1; + mutex_unlock(&ci->lock); + return ecount; +} + +static struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, + .read_data = read_data, + .write_data = write_data, +}; + +static int cxd2099_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cxd *ci; + struct cxd2099_cfg *cfg = client->dev.platform_data; + static const struct regmap_config rm_cfg = { + .reg_bits = 8, + .val_bits = 8, + }; + unsigned int val; + int ret; + + ci = kzalloc(sizeof(*ci), GFP_KERNEL); + if (!ci) { + ret = -ENOMEM; + goto err; + } + + ci->client = client; + memcpy(&ci->cfg, cfg, sizeof(ci->cfg)); + + ci->regmap = regmap_init_i2c(client, &rm_cfg); + if (IS_ERR(ci->regmap)) { + ret = PTR_ERR(ci->regmap); + goto err_kfree; + } + + ret = regmap_read(ci->regmap, 0x00, &val); + if (ret < 0) { + dev_info(&client->dev, "No CXD2099AR detected at 0x%02x\n", + client->addr); + goto err_rmexit; + } + + mutex_init(&ci->lock); + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + + ci->en = en_templ; + ci->en.data = ci; + init(ci); + dev_info(&client->dev, "Attached CXD2099AR at 0x%02x\n", client->addr); + + *cfg->en = &ci->en; + + if (!buffermode) { + ci->en.read_data = NULL; + ci->en.write_data = NULL; + } else { + dev_info(&client->dev, "Using CXD2099AR buffer mode"); + } + + i2c_set_clientdata(client, ci); + + return 0; + +err_rmexit: + regmap_exit(ci->regmap); +err_kfree: + kfree(ci); +err: + + return ret; +} + +static int cxd2099_remove(struct i2c_client *client) +{ + struct cxd *ci = i2c_get_clientdata(client); + + regmap_exit(ci->regmap); + kfree(ci); + + return 0; +} + +static const struct i2c_device_id cxd2099_id[] = { + {"cxd2099", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cxd2099_id); + +static struct i2c_driver cxd2099_driver = { + .driver = { + .name = "cxd2099", + }, + .probe = cxd2099_probe, + .remove = cxd2099_remove, + .id_table = cxd2099_id, +}; + +module_i2c_driver(cxd2099_driver); + +MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h new file mode 100644 index 000000000000..8fa45a4c615a --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2099.h @@ -0,0 +1,32 @@ +/* + * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010-2011 Digital Devices GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 _CXD2099_H_ +#define _CXD2099_H_ + +#include <media/dvb_ca_en50221.h> + +struct cxd2099_cfg { + u32 bitrate; + u8 polarity; + u8 clock_mode; + + u32 max_i2c; + + /* ptr to DVB CA struct */ + struct dvb_ca_en50221 **en; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig new file mode 100644 index 000000000000..9d989676e800 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DVB_CXD2880 + tristate "Sony CXD2880 DVB-T2/T tuner + demodulator" + depends on DVB_CORE && SPI + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend.
\ No newline at end of file diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile new file mode 100644 index 000000000000..c6baa4caba19 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 + +cxd2880-objs := cxd2880_common.o \ + cxd2880_devio_spi.o \ + cxd2880_integ.o \ + cxd2880_io.o \ + cxd2880_spi_device.o \ + cxd2880_tnrdmd.o \ + cxd2880_tnrdmd_dvbt2.o \ + cxd2880_tnrdmd_dvbt2_mon.o \ + cxd2880_tnrdmd_dvbt.o \ + cxd2880_tnrdmd_dvbt_mon.o\ + cxd2880_tnrdmd_mon.o\ + cxd2880_top.o + +obj-$(CONFIG_DVB_CXD2880) += cxd2880.o + +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h new file mode 100644 index 000000000000..4ea3510aab66 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_H +#define CXD2880_H + +struct cxd2880_config { + struct spi_device *spi; + struct mutex *spi_mutex; /* For SPI access exclusive control */ +}; + +#if IS_REACHABLE(CONFIG_DVB_CXD2880) +extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg); +#else +static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_CXD2880 */ + +#endif /* CXD2880_H */ diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c new file mode 100644 index 000000000000..d6f5af6609c1 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_common.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_common.h" + +int cxd2880_convert2s_complement(u32 value, u32 bitlen) +{ + if (!bitlen || bitlen >= 32) + return (int)value; + + if (value & (u32)(1 << (bitlen - 1))) + return (int)(GENMASK(31, bitlen) | value); + else + return (int)(GENMASK(bitlen - 1, 0) & value); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h new file mode 100644 index 000000000000..b05bce71ab35 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_common.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_COMMON_H +#define CXD2880_COMMON_H + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/string.h> + +int cxd2880_convert2s_complement(u32 value, u32 bitlen); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c new file mode 100644 index 000000000000..aba59400859e --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_devio_spi.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * I/O interface via SPI + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_devio_spi.h" + +#define BURST_WRITE_MAX 128 + +static int cxd2880_io_spi_read_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 *data, + u32 size) +{ + int ret = 0; + struct cxd2880_spi *spi = NULL; + u8 send_data[6]; + u8 *read_data_top = data; + + if (!io || !io->if_object || !data) + return -EINVAL; + + if (sub_address + size > 0x100) + return -EINVAL; + + spi = io->if_object; + + if (tgt == CXD2880_IO_TGT_SYS) + send_data[0] = 0x0b; + else + send_data[0] = 0x0a; + + send_data[3] = 0; + send_data[4] = 0; + send_data[5] = 0; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = size; + + ret = + spi->write_read(spi, send_data, sizeof(send_data), + read_data_top, send_data[2]); + if (ret) + return ret; + + sub_address += send_data[2]; + read_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +static int cxd2880_io_spi_write_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, + const u8 *data, u32 size) +{ + int ret = 0; + struct cxd2880_spi *spi = NULL; + u8 send_data[BURST_WRITE_MAX + 4]; + const u8 *write_data_top = data; + + if (!io || !io->if_object || !data) + return -EINVAL; + + if (size > BURST_WRITE_MAX) + return -EINVAL; + + if (sub_address + size > 0x100) + return -EINVAL; + + spi = io->if_object; + + if (tgt == CXD2880_IO_TGT_SYS) + send_data[0] = 0x0f; + else + send_data[0] = 0x0e; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = size; + + memcpy(&send_data[3], write_data_top, send_data[2]); + + if (tgt == CXD2880_IO_TGT_SYS) { + send_data[3 + send_data[2]] = 0x00; + ret = spi->write(spi, send_data, send_data[2] + 4); + } else { + ret = spi->write(spi, send_data, send_data[2] + 3); + } + if (ret) + return ret; + + sub_address += send_data[2]; + write_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +int cxd2880_io_spi_create(struct cxd2880_io *io, + struct cxd2880_spi *spi, u8 slave_select) +{ + if (!io || !spi) + return -EINVAL; + + io->read_regs = cxd2880_io_spi_read_reg; + io->write_regs = cxd2880_io_spi_write_reg; + io->write_reg = cxd2880_io_common_write_one_reg; + io->if_object = spi; + io->i2c_address_sys = 0; + io->i2c_address_demod = 0; + io->slave_select = slave_select; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h new file mode 100644 index 000000000000..27f7cb12fad4 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_devio_spi.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * I/O interface via SPI + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DEVIO_SPI_H +#define CXD2880_DEVIO_SPI_H + +#include "cxd2880_common.h" +#include "cxd2880_io.h" +#include "cxd2880_spi.h" + +#include "cxd2880_tnrdmd.h" + +int cxd2880_io_spi_create(struct cxd2880_io *io, + struct cxd2880_spi *spi, + u8 slave_select); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h new file mode 100644 index 000000000000..820f4757a520 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dtv.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DTV related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DTV_H +#define CXD2880_DTV_H + +enum cxd2880_dtv_sys { + CXD2880_DTV_SYS_UNKNOWN, + CXD2880_DTV_SYS_DVBT, + CXD2880_DTV_SYS_DVBT2, + CXD2880_DTV_SYS_ANY +}; + +enum cxd2880_dtv_bandwidth { + CXD2880_DTV_BW_UNKNOWN = 0, + CXD2880_DTV_BW_1_7_MHZ = 1, + CXD2880_DTV_BW_5_MHZ = 5, + CXD2880_DTV_BW_6_MHZ = 6, + CXD2880_DTV_BW_7_MHZ = 7, + CXD2880_DTV_BW_8_MHZ = 8 +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h new file mode 100644 index 000000000000..76a1acc346ef --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dvbt.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DVBT_H +#define CXD2880_DVBT_H + +#include "cxd2880_common.h" + +enum cxd2880_dvbt_constellation { + CXD2880_DVBT_CONSTELLATION_QPSK, + CXD2880_DVBT_CONSTELLATION_16QAM, + CXD2880_DVBT_CONSTELLATION_64QAM, + CXD2880_DVBT_CONSTELLATION_RESERVED_3 +}; + +enum cxd2880_dvbt_hierarchy { + CXD2880_DVBT_HIERARCHY_NON, + CXD2880_DVBT_HIERARCHY_1, + CXD2880_DVBT_HIERARCHY_2, + CXD2880_DVBT_HIERARCHY_4 +}; + +enum cxd2880_dvbt_coderate { + CXD2880_DVBT_CODERATE_1_2, + CXD2880_DVBT_CODERATE_2_3, + CXD2880_DVBT_CODERATE_3_4, + CXD2880_DVBT_CODERATE_5_6, + CXD2880_DVBT_CODERATE_7_8, + CXD2880_DVBT_CODERATE_RESERVED_5, + CXD2880_DVBT_CODERATE_RESERVED_6, + CXD2880_DVBT_CODERATE_RESERVED_7 +}; + +enum cxd2880_dvbt_guard { + CXD2880_DVBT_GUARD_1_32, + CXD2880_DVBT_GUARD_1_16, + CXD2880_DVBT_GUARD_1_8, + CXD2880_DVBT_GUARD_1_4 +}; + +enum cxd2880_dvbt_mode { + CXD2880_DVBT_MODE_2K, + CXD2880_DVBT_MODE_8K, + CXD2880_DVBT_MODE_RESERVED_2, + CXD2880_DVBT_MODE_RESERVED_3 +}; + +enum cxd2880_dvbt_profile { + CXD2880_DVBT_PROFILE_HP = 0, + CXD2880_DVBT_PROFILE_LP +}; + +struct cxd2880_dvbt_tpsinfo { + enum cxd2880_dvbt_constellation constellation; + enum cxd2880_dvbt_hierarchy hierarchy; + enum cxd2880_dvbt_coderate rate_hp; + enum cxd2880_dvbt_coderate rate_lp; + enum cxd2880_dvbt_guard guard; + enum cxd2880_dvbt_mode mode; + u8 fnum; + u8 length_indicator; + u16 cell_id; + u8 cell_id_ok; + u8 reserved_even; + u8 reserved_odd; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h new file mode 100644 index 000000000000..191047b158fe --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dvbt2.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DVBT2_H +#define CXD2880_DVBT2_H + +#include "cxd2880_common.h" + +enum cxd2880_dvbt2_profile { + CXD2880_DVBT2_PROFILE_BASE, + CXD2880_DVBT2_PROFILE_LITE, + CXD2880_DVBT2_PROFILE_ANY +}; + +enum cxd2880_dvbt2_version { + CXD2880_DVBT2_V111, + CXD2880_DVBT2_V121, + CXD2880_DVBT2_V131 +}; + +enum cxd2880_dvbt2_s1 { + CXD2880_DVBT2_S1_BASE_SISO = 0x00, + CXD2880_DVBT2_S1_BASE_MISO = 0x01, + CXD2880_DVBT2_S1_NON_DVBT2 = 0x02, + CXD2880_DVBT2_S1_LITE_SISO = 0x03, + CXD2880_DVBT2_S1_LITE_MISO = 0x04, + CXD2880_DVBT2_S1_RSVD3 = 0x05, + CXD2880_DVBT2_S1_RSVD4 = 0x06, + CXD2880_DVBT2_S1_RSVD5 = 0x07, + CXD2880_DVBT2_S1_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_base_s2 { + CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00, + CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01, + CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02, + CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03, + CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04, + CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05, + CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06, + CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07, + CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_lite_s2 { + CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00, + CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01, + CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02, + CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03, + CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04, + CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05, + CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06, + CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07, + CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_guard { + CXD2880_DVBT2_G1_32 = 0x00, + CXD2880_DVBT2_G1_16 = 0x01, + CXD2880_DVBT2_G1_8 = 0x02, + CXD2880_DVBT2_G1_4 = 0x03, + CXD2880_DVBT2_G1_128 = 0x04, + CXD2880_DVBT2_G19_128 = 0x05, + CXD2880_DVBT2_G19_256 = 0x06, + CXD2880_DVBT2_G_RSVD1 = 0x07, + CXD2880_DVBT2_G_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_mode { + CXD2880_DVBT2_M2K = 0x00, + CXD2880_DVBT2_M8K = 0x01, + CXD2880_DVBT2_M4K = 0x02, + CXD2880_DVBT2_M1K = 0x03, + CXD2880_DVBT2_M16K = 0x04, + CXD2880_DVBT2_M32K = 0x05, + CXD2880_DVBT2_M_RSVD1 = 0x06, + CXD2880_DVBT2_M_RSVD2 = 0x07 +}; + +enum cxd2880_dvbt2_bw { + CXD2880_DVBT2_BW_8 = 0x00, + CXD2880_DVBT2_BW_7 = 0x01, + CXD2880_DVBT2_BW_6 = 0x02, + CXD2880_DVBT2_BW_5 = 0x03, + CXD2880_DVBT2_BW_10 = 0x04, + CXD2880_DVBT2_BW_1_7 = 0x05, + CXD2880_DVBT2_BW_RSVD1 = 0x06, + CXD2880_DVBT2_BW_RSVD2 = 0x07, + CXD2880_DVBT2_BW_RSVD3 = 0x08, + CXD2880_DVBT2_BW_RSVD4 = 0x09, + CXD2880_DVBT2_BW_RSVD5 = 0x0a, + CXD2880_DVBT2_BW_RSVD6 = 0x0b, + CXD2880_DVBT2_BW_RSVD7 = 0x0c, + CXD2880_DVBT2_BW_RSVD8 = 0x0d, + CXD2880_DVBT2_BW_RSVD9 = 0x0e, + CXD2880_DVBT2_BW_RSVD10 = 0x0f, + CXD2880_DVBT2_BW_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1pre_type { + CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00, + CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01, + CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02, + CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03, + CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_papr { + CXD2880_DVBT2_PAPR_0 = 0x00, + CXD2880_DVBT2_PAPR_1 = 0x01, + CXD2880_DVBT2_PAPR_2 = 0x02, + CXD2880_DVBT2_PAPR_3 = 0x03, + CXD2880_DVBT2_PAPR_RSVD1 = 0x04, + CXD2880_DVBT2_PAPR_RSVD2 = 0x05, + CXD2880_DVBT2_PAPR_RSVD3 = 0x06, + CXD2880_DVBT2_PAPR_RSVD4 = 0x07, + CXD2880_DVBT2_PAPR_RSVD5 = 0x08, + CXD2880_DVBT2_PAPR_RSVD6 = 0x09, + CXD2880_DVBT2_PAPR_RSVD7 = 0x0a, + CXD2880_DVBT2_PAPR_RSVD8 = 0x0b, + CXD2880_DVBT2_PAPR_RSVD9 = 0x0c, + CXD2880_DVBT2_PAPR_RSVD10 = 0x0d, + CXD2880_DVBT2_PAPR_RSVD11 = 0x0e, + CXD2880_DVBT2_PAPR_RSVD12 = 0x0f, + CXD2880_DVBT2_PAPR_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_constell { + CXD2880_DVBT2_L1POST_BPSK = 0x00, + CXD2880_DVBT2_L1POST_QPSK = 0x01, + CXD2880_DVBT2_L1POST_QAM16 = 0x02, + CXD2880_DVBT2_L1POST_QAM64 = 0x03, + CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04, + CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05, + CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06, + CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07, + CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08, + CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09, + CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0a, + CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0b, + CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0c, + CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0d, + CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0e, + CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0f, + CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_cr { + CXD2880_DVBT2_L1POST_R1_2 = 0x00, + CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01, + CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02, + CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03, + CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_fec_type { + CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00, + CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01, + CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02, + CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03, + CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_pp { + CXD2880_DVBT2_PP1 = 0x00, + CXD2880_DVBT2_PP2 = 0x01, + CXD2880_DVBT2_PP3 = 0x02, + CXD2880_DVBT2_PP4 = 0x03, + CXD2880_DVBT2_PP5 = 0x04, + CXD2880_DVBT2_PP6 = 0x05, + CXD2880_DVBT2_PP7 = 0x06, + CXD2880_DVBT2_PP8 = 0x07, + CXD2880_DVBT2_PP_RSVD1 = 0x08, + CXD2880_DVBT2_PP_RSVD2 = 0x09, + CXD2880_DVBT2_PP_RSVD3 = 0x0a, + CXD2880_DVBT2_PP_RSVD4 = 0x0b, + CXD2880_DVBT2_PP_RSVD5 = 0x0c, + CXD2880_DVBT2_PP_RSVD6 = 0x0d, + CXD2880_DVBT2_PP_RSVD7 = 0x0e, + CXD2880_DVBT2_PP_RSVD8 = 0x0f, + CXD2880_DVBT2_PP_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_code_rate { + CXD2880_DVBT2_R1_2 = 0x00, + CXD2880_DVBT2_R3_5 = 0x01, + CXD2880_DVBT2_R2_3 = 0x02, + CXD2880_DVBT2_R3_4 = 0x03, + CXD2880_DVBT2_R4_5 = 0x04, + CXD2880_DVBT2_R5_6 = 0x05, + CXD2880_DVBT2_R1_3 = 0x06, + CXD2880_DVBT2_R2_5 = 0x07, + CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_constell { + CXD2880_DVBT2_QPSK = 0x00, + CXD2880_DVBT2_QAM16 = 0x01, + CXD2880_DVBT2_QAM64 = 0x02, + CXD2880_DVBT2_QAM256 = 0x03, + CXD2880_DVBT2_CON_RSVD1 = 0x04, + CXD2880_DVBT2_CON_RSVD2 = 0x05, + CXD2880_DVBT2_CON_RSVD3 = 0x06, + CXD2880_DVBT2_CON_RSVD4 = 0x07, + CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_type { + CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00, + CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01, + CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02, + CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03, + CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04, + CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05, + CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06, + CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07, + CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_payload { + CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00, + CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01, + CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02, + CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0a, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0b, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0c, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0d, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0e, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0f, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1a, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1b, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1c, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1d, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1e, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1f, + CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_fec { + CXD2880_DVBT2_FEC_LDPC_16K = 0x00, + CXD2880_DVBT2_FEC_LDPC_64K = 0x01, + CXD2880_DVBT2_FEC_RSVD1 = 0x02, + CXD2880_DVBT2_FEC_RSVD2 = 0x03, + CXD2880_DVBT2_FEC_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_mode { + CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00, + CXD2880_DVBT2_PLP_MODE_NM = 0x01, + CXD2880_DVBT2_PLP_MODE_HEM = 0x02, + CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03, + CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_btype { + CXD2880_DVBT2_PLP_COMMON, + CXD2880_DVBT2_PLP_DATA +}; + +enum cxd2880_dvbt2_stream { + CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00, + CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01, + CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02, + CXD2880_DVBT2_STREAM_TRANSPORT = 0x03, + CXD2880_DVBT2_STREAM_UNKNOWN = 0xff +}; + +struct cxd2880_dvbt2_l1pre { + enum cxd2880_dvbt2_l1pre_type type; + u8 bw_ext; + enum cxd2880_dvbt2_s1 s1; + u8 s2; + u8 mixed; + enum cxd2880_dvbt2_mode fft_mode; + u8 l1_rep; + enum cxd2880_dvbt2_guard gi; + enum cxd2880_dvbt2_papr papr; + enum cxd2880_dvbt2_l1post_constell mod; + enum cxd2880_dvbt2_l1post_cr cr; + enum cxd2880_dvbt2_l1post_fec_type fec; + u32 l1_post_size; + u32 l1_post_info_size; + enum cxd2880_dvbt2_pp pp; + u8 tx_id_availability; + u16 cell_id; + u16 network_id; + u16 sys_id; + u8 num_frames; + u16 num_symbols; + u8 regen; + u8 post_ext; + u8 num_rf_freqs; + u8 rf_idx; + enum cxd2880_dvbt2_version t2_version; + u8 l1_post_scrambled; + u8 t2_base_lite; + u32 crc32; +}; + +struct cxd2880_dvbt2_plp { + u8 id; + enum cxd2880_dvbt2_plp_type type; + enum cxd2880_dvbt2_plp_payload payload; + u8 ff; + u8 first_rf_idx; + u8 first_frm_idx; + u8 group_id; + enum cxd2880_dvbt2_plp_constell constell; + enum cxd2880_dvbt2_plp_code_rate plp_cr; + u8 rot; + enum cxd2880_dvbt2_plp_fec fec; + u16 num_blocks_max; + u8 frm_int; + u8 til_len; + u8 til_type; + u8 in_band_a_flag; + u8 in_band_b_flag; + u16 rsvd; + enum cxd2880_dvbt2_plp_mode plp_mode; + u8 static_flag; + u8 static_padding_flag; +}; + +struct cxd2880_dvbt2_l1post { + u16 sub_slices_per_frame; + u8 num_plps; + u8 num_aux; + u8 aux_cfg_rfu; + u8 rf_idx; + u32 freq; + u8 fef_type; + u32 fef_length; + u8 fef_intvl; +}; + +struct cxd2880_dvbt2_ofdm { + u8 mixed; + u8 is_miso; + enum cxd2880_dvbt2_mode mode; + enum cxd2880_dvbt2_guard gi; + enum cxd2880_dvbt2_pp pp; + u8 bw_ext; + enum cxd2880_dvbt2_papr papr; + u16 num_symbols; +}; + +struct cxd2880_dvbt2_bbheader { + enum cxd2880_dvbt2_stream stream_input; + u8 is_single_input_stream; + u8 is_constant_coding_modulation; + u8 issy_indicator; + u8 null_packet_deletion; + u8 ext; + u8 input_stream_identifier; + u16 user_packet_length; + u16 data_field_length; + u8 sync_byte; + u32 issy; + enum cxd2880_dvbt2_plp_mode plp_mode; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c new file mode 100644 index 000000000000..5302ab0964c1 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_integ.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * integration layer common functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <linux/ktime.h> +#include <linux/errno.h> + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_integ.h" + +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + ktime_t start; + u8 cpu_task_completed = 0; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_tnrdmd_init1(tnr_dmd); + if (ret) + return ret; + + start = ktime_get(); + + while (1) { + ret = + cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (cpu_task_completed) + break; + + if (ktime_to_ms(ktime_sub(ktime_get(), start)) > + CXD2880_TNRDMD_WAIT_INIT_TIMEOUT) + return -ETIMEDOUT; + + usleep_range(CXD2880_TNRDMD_WAIT_INIT_INTVL, + CXD2880_TNRDMD_WAIT_INIT_INTVL + 1000); + } + + return cxd2880_tnrdmd_init2(tnr_dmd); +} + +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 1); + + return 0; +} + +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + if (atomic_read(&tnr_dmd->cancel) != 0) + return -ECANCELED; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h new file mode 100644 index 000000000000..7160225db8b9 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_integ.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * integration layer common interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_INTEG_H +#define CXD2880_INTEG_H + +#include "cxd2880_tnrdmd.h" + +#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT 500 +#define CXD2880_TNRDMD_WAIT_INIT_INTVL 10 + +#define CXD2880_TNRDMD_WAIT_AGC_STABLE 100 + +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd + *tnr_dmd); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c new file mode 100644 index 000000000000..9d932bccfa6c --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_io.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * register I/O interface functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_io.h" + +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data) +{ + if (!io) + return -EINVAL; + + return io->write_regs(io, tgt, sub_address, &data, 1); +} + +int cxd2880_io_set_reg_bits(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data, u8 mask) +{ + int ret; + + if (!io) + return -EINVAL; + + if (mask == 0x00) + return 0; + + if (mask != 0xff) { + u8 rdata = 0x00; + + ret = io->read_regs(io, tgt, sub_address, &rdata, 1); + if (ret) + return ret; + + data = (data & mask) | (rdata & (mask ^ 0xff)); + } + + return io->write_reg(io, tgt, sub_address, data); +} + +int cxd2880_io_write_multi_regs(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + const struct cxd2880_reg_value reg_value[], + u8 size) +{ + int ret; + int i; + + if (!io) + return -EINVAL; + + for (i = 0; i < size ; i++) { + ret = io->write_reg(io, tgt, reg_value[i].addr, + reg_value[i].value); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h new file mode 100644 index 000000000000..ba550278881d --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_io.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * register I/O interface definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_IO_H +#define CXD2880_IO_H + +#include "cxd2880_common.h" + +enum cxd2880_io_tgt { + CXD2880_IO_TGT_SYS, + CXD2880_IO_TGT_DMD +}; + +struct cxd2880_reg_value { + u8 addr; + u8 value; +}; + +struct cxd2880_io { + int (*read_regs)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + u8 *data, u32 size); + int (*write_regs)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + const u8 *data, u32 size); + int (*write_reg)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + u8 data); + void *if_object; + u8 i2c_address_sys; + u8 i2c_address_demod; + u8 slave_select; + void *user; +}; + +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data); + +int cxd2880_io_set_reg_bits(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data, u8 mask); + +int cxd2880_io_write_multi_regs(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + const struct cxd2880_reg_value reg_value[], + u8 size); +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h new file mode 100644 index 000000000000..2be207461847 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_spi.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_SPI_H +#define CXD2880_SPI_H + +#include "cxd2880_common.h" + +enum cxd2880_spi_mode { + CXD2880_SPI_MODE_0, + CXD2880_SPI_MODE_1, + CXD2880_SPI_MODE_2, + CXD2880_SPI_MODE_3 +}; + +struct cxd2880_spi { + int (*read)(struct cxd2880_spi *spi, u8 *data, + u32 size); + int (*write)(struct cxd2880_spi *spi, const u8 *data, + u32 size); + int (*write_read)(struct cxd2880_spi *spi, + const u8 *tx_data, u32 tx_size, + u8 *rx_data, u32 rx_size); + u32 flags; + void *user; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c new file mode 100644 index 000000000000..b8cbaa8d7aff --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_spi_device.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <linux/spi/spi.h> + +#include "cxd2880_spi_device.h" + +static int cxd2880_spi_device_write(struct cxd2880_spi *spi, + const u8 *data, u32 size) +{ + struct cxd2880_spi_device *spi_device = NULL; + struct spi_message msg; + struct spi_transfer tx; + int result = 0; + + if (!spi || !spi->user || !data || size == 0) + return -EINVAL; + + spi_device = spi->user; + + memset(&tx, 0, sizeof(tx)); + tx.tx_buf = data; + tx.len = size; + + spi_message_init(&msg); + spi_message_add_tail(&tx, &msg); + result = spi_sync(spi_device->spi, &msg); + + if (result < 0) + return -EIO; + + return 0; +} + +static int cxd2880_spi_device_write_read(struct cxd2880_spi *spi, + const u8 *tx_data, + u32 tx_size, + u8 *rx_data, + u32 rx_size) +{ + struct cxd2880_spi_device *spi_device = NULL; + int result = 0; + + if (!spi || !spi->user || !tx_data || + !tx_size || !rx_data || !rx_size) + return -EINVAL; + + spi_device = spi->user; + + result = spi_write_then_read(spi_device->spi, tx_data, + tx_size, rx_data, rx_size); + if (result < 0) + return -EIO; + + return 0; +} + +int +cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device, + enum cxd2880_spi_mode mode, + u32 speed_hz) +{ + int result = 0; + struct spi_device *spi = spi_device->spi; + + switch (mode) { + case CXD2880_SPI_MODE_0: + spi->mode = SPI_MODE_0; + break; + case CXD2880_SPI_MODE_1: + spi->mode = SPI_MODE_1; + break; + case CXD2880_SPI_MODE_2: + spi->mode = SPI_MODE_2; + break; + case CXD2880_SPI_MODE_3: + spi->mode = SPI_MODE_3; + break; + default: + return -EINVAL; + } + + spi->max_speed_hz = speed_hz; + spi->bits_per_word = 8; + result = spi_setup(spi); + if (result != 0) { + pr_err("spi_setup failed %d\n", result); + return -EINVAL; + } + + return 0; +} + +int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi, + struct cxd2880_spi_device *spi_device) +{ + if (!spi || !spi_device) + return -EINVAL; + + spi->read = NULL; + spi->write = cxd2880_spi_device_write; + spi->write_read = cxd2880_spi_device_write_read; + spi->flags = 0; + spi->user = spi_device; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h new file mode 100644 index 000000000000..05e3a03de3a3 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_spi_device.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_SPI_DEVICE_H +#define CXD2880_SPI_DEVICE_H + +#include "cxd2880_spi.h" + +struct cxd2880_spi_device { + struct spi_device *spi; +}; + +int cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device, + enum cxd2880_spi_mode mode, + u32 speedHz); + +int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi, + struct cxd2880_spi_device *spi_device); + +#endif /* CXD2880_SPI_DEVICE_H */ diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c new file mode 100644 index 000000000000..4cf2d7cfd3f5 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c @@ -0,0 +1,3519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common control functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt2.h" + +static const struct cxd2880_reg_value p_init1_seq[] = { + {0x11, 0x16}, {0x00, 0x10}, +}; + +static const struct cxd2880_reg_value rf_init1_seq1[] = { + {0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01}, + {0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03}, + {0x1c, 0x00}, +}; + +static const struct cxd2880_reg_value rf_init1_seq2[] = { + {0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e}, +}; + +static const struct cxd2880_reg_value rf_init1_seq3[] = { + {0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11}, + {0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46}, + {0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq4[] = { + {0x15, 0x00}, {0x00, 0x16} +}; + +static const struct cxd2880_reg_value rf_init1_seq5[] = { + {0x00, 0x00}, {0x25, 0x00} +}; + +static const struct cxd2880_reg_value rf_init1_seq6[] = { + {0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, + {0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17} +}; + +static const struct cxd2880_reg_value rf_init1_seq7[] = { + {0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00}, + {0x21, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq8[] = { + {0x00, 0x10}, {0x25, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq9[] = { + {0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00}, +}; + +static const struct cxd2880_reg_value rf_init2_seq1[] = { + {0x00, 0x14}, {0x1b, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init2_seq2[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00}, + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune1_seq1[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune1_seq2[] = { + {0x62, 0x00}, {0x00, 0x15}, +}; + +static const struct cxd2880_reg_value x_tune2_seq1[] = { + {0x00, 0x1a}, {0x29, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune2_seq2[] = { + {0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune2_seq3[] = { + {0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune2_seq4[] = { + {0x00, 0xe1}, {0x8a, 0x87}, +}; + +static const struct cxd2880_reg_value x_tune2_seq5[] = { + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune3_seq[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0}, + {0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune4_seq[] = { + {0x00, 0x00}, {0xfe, 0x01}, +}; + +static const struct cxd2880_reg_value x_sleep1_seq[] = { + {0x00, 0x00}, {0x57, 0x03}, +}; + +static const struct cxd2880_reg_value x_sleep2_seq1[] = { + {0x00, 0x2d}, {0xb1, 0x01}, +}; + +static const struct cxd2880_reg_value x_sleep2_seq2[] = { + {0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00}, + {0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00}, +}; + +static const struct cxd2880_reg_value x_sleep3_seq[] = { + {0x00, 0x00}, {0xfd, 0x00}, +}; + +static const struct cxd2880_reg_value x_sleep4_seq[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00}, + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value spll_reset_seq1[] = { + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01}, + {0x26, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq2[] = { + {0x00, 0x00}, {0x10, 0x00}, +}; + +static const struct cxd2880_reg_value spll_reset_seq3[] = { + {0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq4[] = { + {0x00, 0x00}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq5[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq1[] = { + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq2[] = { + {0x00, 0x00}, {0x10, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq3[] = { + {0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq4[] = { + {0x00, 0x00}, {0x2a, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq5[] = { + {0x00, 0x00}, {0x25, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq6[] = { + {0x00, 0x00}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq7[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value set_ts_pin_seq[] = { + {0x50, 0x3f}, {0x52, 0x1f}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq1[] = { + {0x00, 0x00}, {0x52, 0x00}, +}; + +static const struct cxd2880_reg_value set_ts_output_seq2[] = { + {0x00, 0x00}, {0xc3, 0x00}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq3[] = { + {0x00, 0x00}, {0xc3, 0x01}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq4[] = { + {0x00, 0x00}, {0x52, 0x1f}, + +}; + +static int p_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE || + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_TS: + data = 0x00; + break; + case CXD2880_TNRDMD_TSOUT_IF_SPI: + data = 0x01; + break; + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + data = 0x02; + break; + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + p_init1_seq, + ARRAY_SIZE(p_init1_seq)); + if (ret) + return ret; + + switch (tnr_dmd->chip_id) { + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X: + data = 0x1a; + break; + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11: + data = 0x16; + break; + default: + return -ENOTTY; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data); + if (ret) + return ret; + + if (tnr_dmd->create_param.en_internal_ldo) + data = 0x01; + else + data = 0x00; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, data); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + switch (tnr_dmd->chip_id) { + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X: + data = 0x01; + break; + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11: + data = 0x00; + break; + default: + return -ENOTTY; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x69, data); +} + +static int p_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[6] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = tnr_dmd->create_param.xosc_cap; + data[1] = tnr_dmd->create_param.xosc_i; + switch (tnr_dmd->create_param.xtal_share_type) { + case CXD2880_TNRDMD_XTAL_SHARE_NONE: + data[2] = 0x01; + data[3] = 0x00; + break; + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF: + data[2] = 0x00; + data[3] = 0x00; + break; + case CXD2880_TNRDMD_XTAL_SHARE_MASTER: + data[2] = 0x01; + data[3] = 0x01; + break; + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE: + data[2] = 0x00; + data[3] = 0x01; + break; + default: + return -EINVAL; + } + data[4] = 0x06; + data[5] = 0x00; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data, 6); +} + +static int p_init3(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + switch (tnr_dmd->diver_mode) { + case CXD2880_TNRDMD_DIVERMODE_SINGLE: + data[0] = 0x00; + break; + case CXD2880_TNRDMD_DIVERMODE_MAIN: + data[0] = 0x03; + break; + case CXD2880_TNRDMD_DIVERMODE_SUB: + data[0] = 0x02; + break; + default: + return -EINVAL; + } + + data[1] = 0x01; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1f, data, 2); +} + +static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[8] = { 0 }; + static const u8 rf_init1_cdata1[40] = { + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x04, 0x04, 0x04 + }; + + static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00}; + static const u8 rf_init1_cdata3[80] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09, + 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0a, 0x03, 0xe0 + }; + + static const u8 rf_init1_cdata4[8] = { + 0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80 + }; + + static const u8 rf_init1_cdata5[50] = { + 0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, + 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, + 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x0e + }; + + u8 addr = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x21, data, 3); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + data[0] = 0x01; + data[1] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x17, data, 2); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1a, 0x06); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq1, + ARRAY_SIZE(rf_init1_seq1)); + if (ret) + return ret; + + data[0] = 0x00; + if (tnr_dmd->create_param.is_cxd2881gg && + tnr_dmd->create_param.xtal_share_type == + CXD2880_TNRDMD_XTAL_SHARE_SLAVE) + data[1] = 0x00; + else + data[1] = 0x1f; + data[2] = 0x0a; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xb5, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq2, + ARRAY_SIZE(rf_init1_seq2)); + if (ret) + return ret; + + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) { + data[0] = 0x34; + data[1] = 0x2c; + } else { + data[0] = 0x2f; + data[1] = 0x25; + } + data[2] = 0x15; + data[3] = 0x19; + data[4] = 0x1b; + data[5] = 0x15; + data[6] = 0x19; + data[7] = 0x1b; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xd9, data, 8); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + data[0] = 0x6c; + data[1] = 0x10; + data[2] = 0xa6; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x44, data, 3); + if (ret) + return ret; + data[0] = 0x16; + data[1] = 0xa8; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x50, data, 2); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x22; + data[2] = 0x00; + data[3] = 0x88; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x62, data, 4); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x74, 0x75); + if (ret) + return ret; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x7f, rf_init1_cdata1, 40); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x16); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x71; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 2); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x23, 0x89); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x27, rf_init1_cdata2, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x3a, rf_init1_cdata3, 80); + if (ret) + return ret; + + data[0] = 0x03; + data[1] = 0xe0; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xbc, data, 2); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq3, + ARRAY_SIZE(rf_init1_seq3)); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + data[0] = 0x06; + data[1] = 0x07; + data[2] = 0x1a; + } else { + data[0] = 0x00; + data[1] = 0x08; + data[2] = 0x19; + } + data[3] = 0x0e; + data[4] = 0x09; + data[5] = 0x0e; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x12); + if (ret) + return ret; + for (addr = 0x10; addr < 0x9f; addr += 6) { + if (tnr_dmd->lna_thrs_tbl_air) { + u8 idx = 0; + + idx = (addr - 0x10) / 6; + data[0] = + tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on; + data[1] = + tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + addr, data, 6); + if (ret) + return ret; + } + + data[0] = 0x00; + data[1] = 0x08; + if (tnr_dmd->create_param.stationary_use) + data[2] = 0x1a; + else + data[2] = 0x19; + data[3] = 0x0e; + data[4] = 0x09; + data[5] = 0x0e; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x13); + if (ret) + return ret; + for (addr = 0x10; addr < 0xcf; addr += 6) { + if (tnr_dmd->lna_thrs_tbl_cable) { + u8 idx = 0; + + idx = (addr - 0x10) / 6; + data[0] = + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on; + data[1] = + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + addr, data, 6); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xbd, data, 2); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xc4, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xc9, rf_init1_cdata4, 8); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x14); + if (ret) + return ret; + data[0] = 0x15; + data[1] = 0x18; + data[2] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq4, + ARRAY_SIZE(rf_init1_seq4)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, rf_init1_cdata5, 50); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq5, + ARRAY_SIZE(rf_init1_seq5)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init1_seq6, + ARRAY_SIZE(rf_init1_seq6)); + if (ret) + return ret; + + data[0] = 0x00; + data[1] = 0xfe; + data[2] = 0xee; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6e, data, 3); + if (ret) + return ret; + data[0] = 0xa1; + data[1] = 0x8b; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8d, data, 2); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x77, data, 2); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x80, 0xaa); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init1_seq7, + ARRAY_SIZE(rf_init1_seq7)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq8, + ARRAY_SIZE(rf_init1_seq8)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq9, + ARRAY_SIZE(rf_init1_seq9)); +} + +static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[5] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + data[0] = 0x40; + data[1] = 0x40; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xea, data, 2); + if (ret) + return ret; + + usleep_range(1000, 2000); + + data[0] = 0x00; + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) + data[1] = 0x00; + else + data[1] = 0x01; + data[2] = 0x01; + data[3] = 0x03; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x30, data, 4); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init2_seq1, + ARRAY_SIZE(rf_init2_seq1)); + if (ret) + return ret; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init2_seq2, + ARRAY_SIZE(rf_init2_seq2)); +} + +static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, u32 freq_khz, + enum cxd2880_dtv_bandwidth bandwidth, + u8 is_cable, int shift_frequency_khz) +{ + u8 data[11] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune1_seq1, + ARRAY_SIZE(x_tune1_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + data[2] = 0x0e; + data[4] = 0x03; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xe7, data, 5); + if (ret) + return ret; + + data[0] = 0x1f; + data[1] = 0x80; + data[2] = 0x18; + data[3] = 0x00; + data[4] = 0x07; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xe7, data, 5); + if (ret) + return ret; + + usleep_range(1000, 2000); + + data[0] = 0x72; + data[1] = 0x81; + data[3] = 0x1d; + data[4] = 0x6f; + data[5] = 0x7e; + data[7] = 0x1c; + switch (sys) { + case CXD2880_DTV_SYS_DVBT: + data[2] = 0x94; + data[6] = 0x91; + break; + case CXD2880_DTV_SYS_DVBT2: + data[2] = 0x96; + data[6] = 0x93; + break; + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x44, data, 8); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune1_seq2, + ARRAY_SIZE(x_tune1_seq2)); + if (ret) + return ret; + + data[0] = 0x03; + data[1] = 0xe2; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1e, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + data[0] = is_cable ? 0x01 : 0x00; + data[1] = 0x00; + data[2] = 0x6b; + data[3] = 0x4d; + + switch (bandwidth) { + case CXD2880_DTV_BW_1_7_MHZ: + data[4] = 0x03; + break; + case CXD2880_DTV_BW_5_MHZ: + case CXD2880_DTV_BW_6_MHZ: + data[4] = 0x00; + break; + case CXD2880_DTV_BW_7_MHZ: + data[4] = 0x01; + break; + case CXD2880_DTV_BW_8_MHZ: + data[4] = 0x02; + break; + default: + return -EINVAL; + } + + data[5] = 0x00; + + freq_khz += shift_frequency_khz; + + data[6] = (freq_khz >> 16) & 0x0f; + data[7] = (freq_khz >> 8) & 0xff; + data[8] = freq_khz & 0xff; + data[9] = 0xff; + data[10] = 0xfe; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x52, data, 11); +} + +static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_bandwidth bandwidth, + enum cxd2880_tnrdmd_clockmode clk_mode, + int shift_frequency_khz) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + + data[0] = 0x01; + data[1] = 0x0e; + data[2] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2d, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune2_seq1, + ARRAY_SIZE(x_tune2_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2c, data, 1); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x60, data[0]); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune2_seq2, + ARRAY_SIZE(x_tune2_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq3, + ARRAY_SIZE(x_tune2_seq3)); + if (ret) + return ret; + + if (shift_frequency_khz != 0) { + int shift_freq = 0; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0xe1); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 2); + if (ret) + return ret; + + shift_freq = shift_frequency_khz * 1000; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = (shift_freq + 183 / 2) / 183; + else + shift_freq = (shift_freq - 183 / 2) / 183; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = (shift_freq + 178 / 2) / 178; + else + shift_freq = (shift_freq - 178 / 2) / 178; + break; + } + + shift_freq += + cxd2880_convert2s_complement((data[0] << 8) | data[1], 16); + + if (shift_freq > 32767) + shift_freq = 32767; + else if (shift_freq < -32768) + shift_freq = -32768; + + data[0] = (shift_freq >> 8) & 0xff; + data[1] = shift_freq & 0xff; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x69, data, 1); + if (ret) + return ret; + + shift_freq = -shift_frequency_khz; + + if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 17578 / 2) / 17578; + else + shift_freq = + (shift_freq * 1000 - + 17578 / 2) / 17578; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 17090 / 2) / 17090; + else + shift_freq = + (shift_freq * 1000 - + 17090 / 2) / 17090; + break; + } + } else { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 35156 / 2) / 35156; + else + shift_freq = + (shift_freq * 1000 - + 35156 / 2) / 35156; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 34180 / 2) / 34180; + else + shift_freq = + (shift_freq * 1000 - + 34180 / 2) / 34180; + break; + } + } + + shift_freq += cxd2880_convert2s_complement(data[0], 8); + + if (shift_freq > 127) + shift_freq = 127; + else if (shift_freq < -128) + shift_freq = -128; + + data[0] = shift_freq & 0xff; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x69, data[0]); + if (ret) + return ret; + } + + if (tnr_dmd->create_param.stationary_use) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq4, + ARRAY_SIZE(x_tune2_seq4)); + if (ret) + return ret; + } + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq5, + ARRAY_SIZE(x_tune2_seq5)); +} + +static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl) +{ + u8 data[6] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune3_seq, + ARRAY_SIZE(x_tune3_seq)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl) + memset(data, 0x01, sizeof(data)); + else + memset(data, 0x00, sizeof(data)); + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xef, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2d); + if (ret) + return ret; + if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl) + data[0] = 0x00; + else + data[0] = 0x01; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb1, data[0]); +} + +static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x14; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x0b; + data[1] = 0xff; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x57, 0x01); + if (ret) + return ret; + data[0] = 0x0b; + data[1] = 0xff; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x14; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x57, 0x02); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune4_seq, + ARRAY_SIZE(x_tune4_seq)); + if (ret) + return ret; + + return cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_DMD, + x_tune4_seq, + ARRAY_SIZE(x_tune4_seq)); +} + +static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_sleep1_seq, + ARRAY_SIZE(x_sleep1_seq)); + if (ret) + return ret; + + data[0] = 0x00; + data[1] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x1f; + data[1] = 0xff; + data[2] = 0x03; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 3); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x1f; + data[1] = 0xff; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); +} + +static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep2_seq1, + ARRAY_SIZE(x_sleep2_seq1)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb2, &data, 1); + if (ret) + return ret; + + if ((data & 0x01) == 0x00) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_sleep2_seq2, + ARRAY_SIZE(x_sleep2_seq2)); +} + +static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep3_seq, + ARRAY_SIZE(x_sleep3_seq)); +} + +static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep4_seq, + ARRAY_SIZE(x_sleep4_seq)); +} + +static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_clockmode clockmode) +{ + u8 data[4] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq1, + ARRAY_SIZE(spll_reset_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + spll_reset_seq2, + ARRAY_SIZE(spll_reset_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq3, + ARRAY_SIZE(spll_reset_seq3)); + if (ret) + return ret; + + switch (clockmode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data[0] = 0x00; + break; + + case CXD2880_TNRDMD_CLOCKMODE_B: + data[0] = 0x01; + break; + + case CXD2880_TNRDMD_CLOCKMODE_C: + data[0] = 0x02; + break; + + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x30, data[0]); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x22, 0x00); + if (ret) + return ret; + + usleep_range(2000, 3000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq4, + ARRAY_SIZE(spll_reset_seq4)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + spll_reset_seq5, + ARRAY_SIZE(spll_reset_seq5)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + memset(data, 0x00, sizeof(data)); + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x26, data, 4); +} + +static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq1, + ARRAY_SIZE(t_power_x_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + t_power_x_seq2, + ARRAY_SIZE(t_power_x_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq3, + ARRAY_SIZE(t_power_x_seq3)); + if (ret) + return ret; + + if (on) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2b, 0x01); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq4, + ARRAY_SIZE(t_power_x_seq4)); + if (ret) + return ret; + } else { + data[0] = 0x03; + data[1] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2a, data, 2); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq5, + ARRAY_SIZE(t_power_x_seq5)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq6, + ARRAY_SIZE(t_power_x_seq6)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + t_power_x_seq7, + ARRAY_SIZE(t_power_x_seq7)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + memset(data, 0x00, sizeof(data)); + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x27, data, 3); +} + +struct cxd2880_tnrdmd_ts_clk_cfg { + u8 srl_clk_mode; + u8 srl_duty_mode; + u8 ts_clk_period; +}; + +static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys) +{ + int ret; + u8 backwards_compatible = 0; + struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg; + u8 ts_rate_ctrl_off = 0; + u8 ts_in_off = 0; + u8 ts_clk_manaul_on = 0; + u8 data = 0; + + static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = { + { + {3, 1, 8,}, + {0, 2, 16,} + }, { + {1, 1, 8,}, + {2, 2, 16,} + } + }; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + if (tnr_dmd->is_ts_backwards_compatible_mode) { + backwards_compatible = 1; + ts_rate_ctrl_off = 1; + ts_in_off = 1; + } else { + backwards_compatible = 0; + ts_rate_ctrl_off = 0; + ts_in_off = 0; + } + + if (tnr_dmd->ts_byte_clk_manual_setting) { + ts_clk_manaul_on = 1; + ts_rate_ctrl_off = 0; + } + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd3, ts_rate_ctrl_off, 0x01); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xde, ts_in_off, 0x01); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xda, ts_clk_manaul_on, 0x01); + if (ret) + return ret; + + ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts] + [tnr_dmd->srl_ts_clk_frq]; + + if (tnr_dmd->ts_byte_clk_manual_setting) + ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc4, ts_clk_cfg.srl_clk_mode, 0x03); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd1, ts_clk_cfg.srl_duty_mode, 0x03); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, 0xd9, + ts_clk_cfg.ts_clk_period); + if (ret) + return ret; + + data = backwards_compatible ? 0x00 : 0x01; + + if (sys == CXD2880_DTV_SYS_DVBT) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = + cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x66, data, 0x01); + } + + return ret; +} + +static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg) +{ + int i; + int ret; + u8 data[65]; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + if (!pid_ftr_cfg) + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x50, 0x02); + + data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00; + + for (i = 0; i < 32; i++) { + if (pid_ftr_cfg->pid_cfg[i].is_en) { + data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20; + data[2 + (i * 2)] = pid_ftr_cfg->pid_cfg[i].pid & 0xff; + } else { + data[1 + (i * 2)] = 0x00; + data[2 + (i * 2)] = 0x00; + } + } + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x50, data, 65); +} + +static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + u8 i; + + if (!tnr_dmd) + return -EINVAL; + + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + tnr_dmd->cfg_mem[i].tgt, + 0x00, tnr_dmd->cfg_mem[i].bank); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + tnr_dmd->cfg_mem[i].tgt, + tnr_dmd->cfg_mem[i].address, + tnr_dmd->cfg_mem[i].value, + tnr_dmd->cfg_mem[i].bit_mask); + if (ret) + return ret; + } + + return 0; +} + +static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, u8 value, u8 bit_mask) +{ + u8 i; + u8 value_stored = 0; + + if (!tnr_dmd) + return -EINVAL; + + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) { + if (value_stored == 0 && + tnr_dmd->cfg_mem[i].tgt == tgt && + tnr_dmd->cfg_mem[i].bank == bank && + tnr_dmd->cfg_mem[i].address == address) { + tnr_dmd->cfg_mem[i].value &= ~bit_mask; + tnr_dmd->cfg_mem[i].value |= (value & bit_mask); + + tnr_dmd->cfg_mem[i].bit_mask |= bit_mask; + + value_stored = 1; + } + } + + if (value_stored) + return 0; + + if (tnr_dmd->cfg_mem_last_entry < CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) { + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask); + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask; + tnr_dmd->cfg_mem_last_entry++; + } else { + return -ENOMEM; + } + + return 0; +} + +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_io *io, + struct cxd2880_tnrdmd_create_param + *create_param) +{ + if (!tnr_dmd || !io || !create_param) + return -EINVAL; + + memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd)); + + tnr_dmd->io = io; + tnr_dmd->create_param = *create_param; + + tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE; + tnr_dmd->diver_sub = NULL; + + tnr_dmd->srl_ts_clk_mod_cnts = 1; + tnr_dmd->en_fef_intmtnt_base = 1; + tnr_dmd->en_fef_intmtnt_lite = 1; + tnr_dmd->rf_lvl_cmpstn = NULL; + tnr_dmd->lna_thrs_tbl_air = NULL; + tnr_dmd->lna_thrs_tbl_cable = NULL; + atomic_set(&tnr_dmd->cancel, 0); + + return 0; +} + +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd + *tnr_dmd_main, + struct cxd2880_io *io_main, + struct cxd2880_tnrdmd *tnr_dmd_sub, + struct cxd2880_io *io_sub, + struct + cxd2880_tnrdmd_diver_create_param + *create_param) +{ + struct cxd2880_tnrdmd_create_param *main_param, *sub_param; + + if (!tnr_dmd_main || !io_main || !tnr_dmd_sub || !io_sub || + !create_param) + return -EINVAL; + + memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd)); + memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd)); + + main_param = &tnr_dmd_main->create_param; + sub_param = &tnr_dmd_sub->create_param; + + tnr_dmd_main->io = io_main; + tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN; + tnr_dmd_main->diver_sub = tnr_dmd_sub; + tnr_dmd_main->create_param.en_internal_ldo = + create_param->en_internal_ldo; + + main_param->ts_output_if = create_param->ts_output_if; + main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER; + main_param->xosc_cap = create_param->xosc_cap_main; + main_param->xosc_i = create_param->xosc_i_main; + main_param->is_cxd2881gg = create_param->is_cxd2881gg; + main_param->stationary_use = create_param->stationary_use; + + tnr_dmd_sub->io = io_sub; + tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB; + tnr_dmd_sub->diver_sub = NULL; + + sub_param->en_internal_ldo = create_param->en_internal_ldo; + sub_param->ts_output_if = create_param->ts_output_if; + sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE; + sub_param->xosc_cap = 0; + sub_param->xosc_i = create_param->xosc_i_sub; + sub_param->is_cxd2881gg = create_param->is_cxd2881gg; + sub_param->stationary_use = create_param->stationary_use; + + tnr_dmd_main->srl_ts_clk_mod_cnts = 1; + tnr_dmd_main->en_fef_intmtnt_base = 1; + tnr_dmd_main->en_fef_intmtnt_lite = 1; + tnr_dmd_main->rf_lvl_cmpstn = NULL; + tnr_dmd_main->lna_thrs_tbl_air = NULL; + tnr_dmd_main->lna_thrs_tbl_cable = NULL; + + tnr_dmd_sub->srl_ts_clk_mod_cnts = 1; + tnr_dmd_sub->en_fef_intmtnt_base = 1; + tnr_dmd_sub->en_fef_intmtnt_lite = 1; + tnr_dmd_sub->rf_lvl_cmpstn = NULL; + tnr_dmd_sub->lna_thrs_tbl_air = NULL; + tnr_dmd_sub->lna_thrs_tbl_cable = NULL; + + return 0; +} + +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd || tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN; + tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN; + tnr_dmd->frequency_khz = 0; + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN; + tnr_dmd->scan_mode = 0; + atomic_set(&tnr_dmd->cancel, 0); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN; + tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN; + tnr_dmd->diver_sub->frequency_khz = 0; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN; + tnr_dmd->diver_sub->scan_mode = 0; + atomic_set(&tnr_dmd->diver_sub->cancel, 0); + } + + ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id); + if (ret) + return ret; + + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id)) + return -ENOTTY; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub, + &tnr_dmd->diver_sub->chip_id); + if (ret) + return ret; + + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id)) + return -ENOTTY; + } + + ret = p_init1(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init1(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + usleep_range(1000, 2000); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = p_init2(tnr_dmd); + if (ret) + return ret; + + usleep_range(5000, 6000); + + ret = p_init3(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init3(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = rf_init1(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = rf_init1(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 cpu_task_completed; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (!cpu_task_completed) + return -EINVAL; + + ret = rf_init2(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = rf_init2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = load_cfg_mem(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = load_cfg_mem(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP; + + return ret; +} + +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *task_completed) +{ + u16 cpu_status = 0; + int ret; + + if (!tnr_dmd || !task_completed) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (cpu_status == 0) + *task_completed = 1; + else + *task_completed = 0; + + return ret; + } + if (cpu_status != 0) { + *task_completed = 0; + return ret; + } + + ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status); + if (ret) + return ret; + + if (cpu_status == 0) + *task_completed = 1; + else + *task_completed = 0; + + return ret; +} + +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u32 frequency_khz, + enum cxd2880_dtv_bandwidth + bandwidth, u8 one_seg_opt, + u8 one_seg_opt_shft_dir) +{ + u8 data; + enum cxd2880_tnrdmd_clockmode new_clk_mode = + CXD2880_TNRDMD_CLOCKMODE_A; + int shift_frequency_khz; + u8 cpu_task_completed; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (frequency_khz < 4000) + return -EINVAL; + + ret = cxd2880_tnrdmd_sleep(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, + 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2b, + &data, + 1); + if (ret) + return ret; + + switch (sys) { + case CXD2880_DTV_SYS_DVBT: + if (data == 0x00) { + ret = t_power_x(tnr_dmd, 1); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = t_power_x(tnr_dmd->diver_sub, 1); + if (ret) + return ret; + } + } + break; + + case CXD2880_DTV_SYS_DVBT2: + if (data == 0x01) { + ret = t_power_x(tnr_dmd, 0); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = t_power_x(tnr_dmd->diver_sub, 0); + if (ret) + return ret; + } + } + break; + + default: + return -EINVAL; + } + + ret = spll_reset(tnr_dmd, new_clk_mode); + if (ret) + return ret; + + tnr_dmd->clk_mode = new_clk_mode; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode); + if (ret) + return ret; + + tnr_dmd->diver_sub->clk_mode = new_clk_mode; + } + + ret = load_cfg_mem(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = load_cfg_mem(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + if (one_seg_opt) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + shift_frequency_khz = 350; + } else { + if (one_seg_opt_shft_dir) + shift_frequency_khz = 350; + else + shift_frequency_khz = -350; + + if (tnr_dmd->create_param.xtal_share_type == + CXD2880_TNRDMD_XTAL_SHARE_SLAVE) + shift_frequency_khz *= -1; + } + } else { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + shift_frequency_khz = 150; + } else { + switch (tnr_dmd->create_param.xtal_share_type) { + case CXD2880_TNRDMD_XTAL_SHARE_NONE: + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF: + default: + shift_frequency_khz = 0; + break; + case CXD2880_TNRDMD_XTAL_SHARE_MASTER: + shift_frequency_khz = 150; + break; + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE: + shift_frequency_khz = -150; + break; + } + } + } + + ret = + x_tune1(tnr_dmd, sys, frequency_khz, bandwidth, + tnr_dmd->is_cable_input, shift_frequency_khz); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune1(tnr_dmd->diver_sub, sys, frequency_khz, + bandwidth, tnr_dmd->is_cable_input, + -shift_frequency_khz); + if (ret) + return ret; + } + + usleep_range(10000, 11000); + + ret = + cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (!cpu_task_completed) + return -EINVAL; + + ret = + x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode, + shift_frequency_khz); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune2(tnr_dmd->diver_sub, bandwidth, + tnr_dmd->diver_sub->clk_mode, + -shift_frequency_khz); + if (ret) + return ret; + } + + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) { + ret = set_ts_clk_mode_and_freq(tnr_dmd, sys); + } else { + struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg; + + if (tnr_dmd->pid_ftr_cfg_en) + pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg; + else + pid_ftr_cfg = NULL; + + ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg); + } + + return ret; +} + +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl); + if (ret) + return ret; + ret = x_tune4(tnr_dmd); + if (ret) + return ret; + } + + return cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1); +} + +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP) + return 0; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep1(tnr_dmd); + if (ret) + return ret; + } + + ret = x_sleep2(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + switch (tnr_dmd->sys) { + case CXD2880_DTV_SYS_DVBT: + ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd); + if (ret) + return ret; + break; + + case CXD2880_DTV_SYS_DVBT2: + ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + ret = x_sleep3(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep3(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = x_sleep4(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep4(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP; + tnr_dmd->frequency_khz = 0; + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP; + tnr_dmd->diver_sub->frequency_khz = 0; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN; + } + + return 0; +} + +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_cfg_id id, + int value) +{ + int ret = 0; + u8 data[2] = { 0 }; + u8 need_sub_setting = 0; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (id) { + case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc4, + value ? 0x00 : 0x10, + 0x10); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x00 : 0x02, + 0x02); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x00 : 0x04, + 0x04); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xcb, + value ? 0x00 : 0x01, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_CONT: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc6, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSVALID_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc8, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc9, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x91, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_CURRENT: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x51, value, + 0x3f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x50, + value ? 0x80 : 0x00, + 0x80); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x50, value, + 0x3f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_FREQ: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 1) + return -EINVAL; + + tnr_dmd->srl_ts_clk_frq = + (enum cxd2880_tnrdmd_serial_ts_clk)value; + break; + + case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0xff) + return -EINVAL; + + tnr_dmd->ts_byte_clk_manual_setting = value; + + break; + + case CXD2880_TNRDMD_CFG_TS_PACKET_GAP: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 7) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xd6, value, + 0x07); + if (ret) + return ret; + + break; + + case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0; + + break; + + case CXD2880_TNRDMD_CFG_PWM_VALUE: + if (value < 0 || value > 0x1000) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x22, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + + data[0] = (value >> 8) & 0x1f; + data[1] = value & 0xff; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x23, + data[0], 0x1f); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x24, + data[1], 0xff); + if (ret) + return ret; + + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT: + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x48, data[0], + 0xff); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x49, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL: + data[0] = value & 0x07; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x4a, data[0], + 0x07); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL: + data[0] = (value & 0x07) << 3; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x4a, data[0], + 0x38); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE: + if (value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN || + value > CXD2880_TNRDMD_CLOCKMODE_C) + return -EINVAL; + tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value; + break; + + case CXD2880_TNRDMD_CFG_CABLE_INPUT: + tnr_dmd->is_cable_input = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE: + tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE: + tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x99, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9a, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9b, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9c, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9d, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9e, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST: + tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD: + if (value < 0 || value > 31) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x60, + value & 0x1f, 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD: + if (value < 0 || value > 7) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x6f, + value & 0x07, 0x07); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x20, 0x72, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x20, 0x6f, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT_PER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x5c, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_PER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x24, 0xdc, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + if (need_sub_setting && + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value); + + return ret; +} + +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode mode, + u8 open_drain, u8 invert) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x40 + id, mode, + 0x0f); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x43, + open_drain ? (1 << id) : 0, + 1 << id); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x44, + invert ? (1 << id) : 0, + 1 << id); + if (ret) + return ret; + + return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x45, + en ? 0 : (1 << id), + 1 << id); +} + +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode + mode, u8 open_drain, u8 invert) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode, + open_drain, invert); +} + +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd || !value) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x20, &data, 1); + if (ret) + return ret; + + *value = (data >> id) & 0x01; + + return 0; +} + +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value); +} + +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x46, + value ? (1 << id) : 0, + 1 << id); +} + +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value); +} + +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd, + u16 *value) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd || !value) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, data, 2); + if (ret) + return ret; + + *value = (data[0] << 8) | data[1]; + + return 0; +} + +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd, + u16 value) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x3c, data, 2); +} + +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd, + u8 clear_overflow_flag, + u8 clear_underflow_flag, + u8 clear_buf) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + data[0] = clear_overflow_flag ? 0x02 : 0x00; + data[0] |= clear_underflow_flag ? 0x01 : 0x00; + data[1] = clear_buf ? 0x01 : 0x00; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9f, data, 2); +} + +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_chip_id *chip_id) +{ + int ret; + u8 data = 0; + + if (!tnr_dmd || !chip_id) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xfd, &data, 1); + if (ret) + return ret; + + *chip_id = (enum cxd2880_tnrdmd_chip_id)data; + + return 0; +} + +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, + u8 value, u8 bit_mask) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + tgt, address, value, bit_mask); + if (ret) + return ret; + + return set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask); +} + +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 scan_mode_end) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + tnr_dmd->scan_mode = scan_mode_end; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + return cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys, + scan_mode_end); + else + return 0; +} + +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) + return -ENOTTY; + + if (pid_ftr_cfg) { + tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg; + tnr_dmd->pid_ftr_cfg_en = 1; + } else { + tnr_dmd->pid_ftr_cfg_en = 0; + } + + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) + return pid_ftr_setting(tnr_dmd, pid_ftr_cfg); + else + return 0; +} + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)) +{ + if (!tnr_dmd) + return -EINVAL; + + tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn; + + return 0; +} + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub, + rf_lvl_cmpstn); +} + +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable) +{ + if (!tnr_dmd) + return -EINVAL; + + tnr_dmd->lna_thrs_tbl_air = tbl_air; + tnr_dmd->lna_thrs_tbl_cable = tbl_cable; + + return 0; +} + +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub, + tbl_air, tbl_cable); +} + +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd + *tnr_dmd, u8 en, u8 value) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS) + return -ENOTTY; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + if (en) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x50, ((value & 0x1f) | 0x80)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x52, (value & 0x1f)); + } else { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_pin_seq, + ARRAY_SIZE(set_ts_pin_seq)); + if (ret) + return ret; + + ret = load_cfg_mem(tnr_dmd); + } + + return ret; +} + +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd, + u8 en) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_TS: + if (en) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_output_seq1, + ARRAY_SIZE(set_ts_output_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + set_ts_output_seq2, + ARRAY_SIZE(set_ts_output_seq2)); + if (ret) + return ret; + } else { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + set_ts_output_seq3, + ARRAY_SIZE(set_ts_output_seq3)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_output_seq4, + ARRAY_SIZE(set_ts_output_seq4)); + if (ret) + return ret; + } + break; + + case CXD2880_TNRDMD_TSOUT_IF_SPI: + break; + + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + break; + + default: + return -EINVAL; + } + + return 0; +} + +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_SPI: + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, &data, 1); + if (ret) + return ret; + + break; + case CXD2880_TNRDMD_TSOUT_IF_TS: + default: + break; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x01, 0x01); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h new file mode 100644 index 000000000000..9d809a251fc7 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common control interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_H +#define CXD2880_TNRDMD_H + +#include <linux/atomic.h> + +#include "cxd2880_common.h" +#include "cxd2880_io.h" +#include "cxd2880_dtv.h" +#include "cxd2880_dvbt.h" +#include "cxd2880_dvbt2.h" + +#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100 + +#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\ +((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00))) + +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW 0x0001 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW 0x0002 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY 0x0004 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL 0x0008 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY 0x0010 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND 0x0020 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS 0x0040 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR 0x0100 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK 0x0200 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK 0x0400 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM 0x0800 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS 0x1000 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW 0x2000 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL 0x4000 + +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK 0x01 +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK 0x02 +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK 0x04 + +enum cxd2880_tnrdmd_chip_id { + CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00, + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62, + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6a +}; + +#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) \ + (((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \ + ((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) + +enum cxd2880_tnrdmd_state { + CXD2880_TNRDMD_STATE_UNKNOWN, + CXD2880_TNRDMD_STATE_SLEEP, + CXD2880_TNRDMD_STATE_ACTIVE, + CXD2880_TNRDMD_STATE_INVALID +}; + +enum cxd2880_tnrdmd_divermode { + CXD2880_TNRDMD_DIVERMODE_SINGLE, + CXD2880_TNRDMD_DIVERMODE_MAIN, + CXD2880_TNRDMD_DIVERMODE_SUB +}; + +enum cxd2880_tnrdmd_clockmode { + CXD2880_TNRDMD_CLOCKMODE_UNKNOWN, + CXD2880_TNRDMD_CLOCKMODE_A, + CXD2880_TNRDMD_CLOCKMODE_B, + CXD2880_TNRDMD_CLOCKMODE_C +}; + +enum cxd2880_tnrdmd_tsout_if { + CXD2880_TNRDMD_TSOUT_IF_TS, + CXD2880_TNRDMD_TSOUT_IF_SPI, + CXD2880_TNRDMD_TSOUT_IF_SDIO +}; + +enum cxd2880_tnrdmd_xtal_share { + CXD2880_TNRDMD_XTAL_SHARE_NONE, + CXD2880_TNRDMD_XTAL_SHARE_EXTREF, + CXD2880_TNRDMD_XTAL_SHARE_MASTER, + CXD2880_TNRDMD_XTAL_SHARE_SLAVE +}; + +enum cxd2880_tnrdmd_spectrum_sense { + CXD2880_TNRDMD_SPECTRUM_NORMAL, + CXD2880_TNRDMD_SPECTRUM_INV +}; + +enum cxd2880_tnrdmd_cfg_id { + CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB, + CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI, + CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI, + CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI, + CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE, + CXD2880_TNRDMD_CFG_TSCLK_CONT, + CXD2880_TNRDMD_CFG_TSCLK_MASK, + CXD2880_TNRDMD_CFG_TSVALID_MASK, + CXD2880_TNRDMD_CFG_TSERR_MASK, + CXD2880_TNRDMD_CFG_TSERR_VALID_DIS, + CXD2880_TNRDMD_CFG_TSPIN_CURRENT, + CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL, + CXD2880_TNRDMD_CFG_TSPIN_PULLUP, + CXD2880_TNRDMD_CFG_TSCLK_FREQ, + CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL, + CXD2880_TNRDMD_CFG_TS_PACKET_GAP, + CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE, + CXD2880_TNRDMD_CFG_PWM_VALUE, + CXD2880_TNRDMD_CFG_INTERRUPT, + CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL, + CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL, + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS, + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS, + CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS, + CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE, + CXD2880_TNRDMD_CFG_CABLE_INPUT, + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE, + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE, + CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST, + CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD, + CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD, + CXD2880_TNRDMD_CFG_DVBT_PER_MES, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, +}; + +enum cxd2880_tnrdmd_lock_result { + CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT, + CXD2880_TNRDMD_LOCK_RESULT_LOCKED, + CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED +}; + +enum cxd2880_tnrdmd_gpio_mode { + CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00, + CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01, + CXD2880_TNRDMD_GPIO_MODE_INT = 0x02, + CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03, + CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04, + CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05, + CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06 +}; + +enum cxd2880_tnrdmd_serial_ts_clk { + CXD2880_TNRDMD_SERIAL_TS_CLK_FULL, + CXD2880_TNRDMD_SERIAL_TS_CLK_HALF +}; + +struct cxd2880_tnrdmd_cfg_mem { + enum cxd2880_io_tgt tgt; + u8 bank; + u8 address; + u8 value; + u8 bit_mask; +}; + +struct cxd2880_tnrdmd_pid_cfg { + u8 is_en; + u16 pid; +}; + +struct cxd2880_tnrdmd_pid_ftr_cfg { + u8 is_negative; + struct cxd2880_tnrdmd_pid_cfg pid_cfg[32]; +}; + +struct cxd2880_tnrdmd_lna_thrs { + u8 off_on; + u8 on_off; +}; + +struct cxd2880_tnrdmd_lna_thrs_tbl_air { + struct cxd2880_tnrdmd_lna_thrs thrs[24]; +}; + +struct cxd2880_tnrdmd_lna_thrs_tbl_cable { + struct cxd2880_tnrdmd_lna_thrs thrs[32]; +}; + +struct cxd2880_tnrdmd_create_param { + enum cxd2880_tnrdmd_tsout_if ts_output_if; + u8 en_internal_ldo; + enum cxd2880_tnrdmd_xtal_share xtal_share_type; + u8 xosc_cap; + u8 xosc_i; + u8 is_cxd2881gg; + u8 stationary_use; +}; + +struct cxd2880_tnrdmd_diver_create_param { + enum cxd2880_tnrdmd_tsout_if ts_output_if; + u8 en_internal_ldo; + u8 xosc_cap_main; + u8 xosc_i_main; + u8 xosc_i_sub; + u8 is_cxd2881gg; + u8 stationary_use; +}; + +struct cxd2880_tnrdmd { + struct cxd2880_tnrdmd *diver_sub; + struct cxd2880_io *io; + struct cxd2880_tnrdmd_create_param create_param; + enum cxd2880_tnrdmd_divermode diver_mode; + enum cxd2880_tnrdmd_clockmode fixed_clk_mode; + u8 is_cable_input; + u8 en_fef_intmtnt_base; + u8 en_fef_intmtnt_lite; + u8 blind_tune_dvbt2_first; + int (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air; + struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable; + u8 srl_ts_clk_mod_cnts; + enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq; + u8 ts_byte_clk_manual_setting; + u8 is_ts_backwards_compatible_mode; + struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT]; + u8 cfg_mem_last_entry; + struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg; + u8 pid_ftr_cfg_en; + void *user; + enum cxd2880_tnrdmd_chip_id chip_id; + enum cxd2880_tnrdmd_state state; + enum cxd2880_tnrdmd_clockmode clk_mode; + u32 frequency_khz; + enum cxd2880_dtv_sys sys; + enum cxd2880_dtv_bandwidth bandwidth; + u8 scan_mode; + atomic_t cancel; +}; + +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_io *io, + struct cxd2880_tnrdmd_create_param + *create_param); + +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd + *tnr_dmd_main, + struct cxd2880_io *io_main, + struct cxd2880_tnrdmd *tnr_dmd_sub, + struct cxd2880_io *io_sub, + struct + cxd2880_tnrdmd_diver_create_param + *create_param); + +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *task_completed); + +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u32 frequency_khz, + enum cxd2880_dtv_bandwidth + bandwidth, u8 one_seg_opt, + u8 one_seg_opt_shft_dir); + +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl); + +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_cfg_id id, + int value); + +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode mode, + u8 open_drain, u8 invert); + +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode + mode, u8 open_drain, + u8 invert); + +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value); + +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value); + +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value); + +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value); + +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd, + u16 *value); + +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd, + u16 value); + +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd, + u8 clear_overflow_flag, + u8 clear_underflow_flag, + u8 clear_buf); + +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_chip_id *chip_id); + +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, + u8 value, u8 bit_mask); + +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 scan_mode_end); + +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg); + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)); + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)); + +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct + cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable); + +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct + cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable); + +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd + *tnr_dmd, u8 en, u8 value); + +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd, + u8 en); + +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h new file mode 100644 index 000000000000..fab55038b37b --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_driver_version.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * version information + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.4" + +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-01-17" diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c new file mode 100644 index 000000000000..fe3c6f8b1b3e --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control functions for DVB-T + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> + +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" + +static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = { + {0x00, 0x00}, {0x31, 0x01}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = { + {0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03}, + {0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = { + {0x00, 0x12}, {0x44, 0x00}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = { + {0x00, 0x11}, {0x87, 0xd2}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = { + {0x00, 0x00}, {0xfd, 0x01}, +}; + +static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = { + {0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00}, +}; + +static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = { + {0x00, 0x11}, {0x87, 0x04}, +}; + +static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_bandwidth + bandwidth, + enum cxd2880_tnrdmd_clockmode + clk_mode) +{ + static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 }; + static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 }; + static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 }; + static const u8 ratectl_margin[2] = { 0x01, 0xf0 }; + static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 }; + static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa }; + static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 }; + + static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa}; + static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 }; + static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 }; + static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 }; + static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 }; + static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 }; + static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 }; + static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 }; + + static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55}; + static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c }; + static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 }; + static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 }; + static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 }; + static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa }; + static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; + static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 }; + + static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38}; + static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 }; + static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 }; + static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c }; + static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; + static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 }; + static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff }; + static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 }; + + static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99}; + static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa}; + static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d }; + static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 }; + static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 }; + static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 }; + static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 }; + static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc }; + static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 }; + const u8 *data = NULL; + u8 sst_data; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + tune_dmd_setting_seq1, + ARRAY_SIZE(tune_dmd_setting_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = clk_mode_ckffrq_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = clk_mode_ckffrq_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = clk_mode_ckffrq_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x65, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x5d, 0x07); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + u8 data[2] = { 0x01, 0x01 }; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xce, data, 2); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq2, + ARRAY_SIZE(tune_dmd_setting_seq2)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xf0, ratectl_margin, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN || + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq3, + ARRAY_SIZE(tune_dmd_setting_seq3)); + if (ret) + return ret; + } + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq4, + ARRAY_SIZE(tune_dmd_setting_seq4)); + if (ret) + return ret; + } + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = maxclkcnt_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = maxclkcnt_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = maxclkcnt_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x68, data, 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (bandwidth) { + case CXD2880_DTV_BW_8_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x00); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x35; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x34; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw8_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw8_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_7_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x02); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x2f; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x2e; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw7_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw7_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_6_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x29; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x2a; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw6_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw6_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_5_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x06); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x24; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x23; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw5_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw5_notch[2], 2); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq5, + ARRAY_SIZE(tune_dmd_setting_seq5)); +} + +static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + sleep_dmd_setting_seq1, + ARRAY_SIZE(sleep_dmd_setting_seq1)); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + sleep_dmd_setting_seq2, + ARRAY_SIZE(sleep_dmd_setting_seq2)); + + return ret; +} + +static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt_profile profile) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x67, + (profile == CXD2880_DVBT_PROFILE_HP) + ? 0x00 : 0x01); +} + +int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT, + tune_param->center_freq_khz, + tune_param->bandwidth, 0, 0); + if (ret) + return ret; + + ret = + x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth, + tnr_dmd->clk_mode); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune_dvbt_demod_setting(tnr_dmd->diver_sub, + tune_param->bandwidth, + tnr_dmd->diver_sub->clk_mode); + if (ret) + return ret; + } + + return dvbt_set_profile(tnr_dmd, tune_param->profile); +} + +int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT, + 0); + if (ret) + return ret; + + tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->sys = CXD2880_DTV_SYS_DVBT; + tnr_dmd->bandwidth = tune_param->bandwidth; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT; + tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_sleep_dvbt_demod_setting(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; + } + + if (sync_stat == 6) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return ret; + } + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (ts_lock) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; + } + + if (ts_lock) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return ret; + } else if (!unlock_detected) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + return ret; + } + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h new file mode 100644 index 000000000000..35d81ccc732b --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control interface for DVB-T + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT_H +#define CXD2880_TNRDMD_DVBT_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +struct cxd2880_dvbt_tune_param { + u32 center_freq_khz; + enum cxd2880_dtv_bandwidth bandwidth; + enum cxd2880_dvbt_profile profile; +}; + +int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c new file mode 100644 index 000000000000..dd32004a12d8 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c @@ -0,0 +1,1217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt2.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control functions for DVB-T2 + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> + +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" + +static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = { + {0x00, 0x00}, {0x31, 0x02}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = { + {0x00, 0x04}, {0x5d, 0x0b}, +}; + +static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_bandwidth + bandwidth, + enum cxd2880_tnrdmd_clockmode + clk_mode) +{ + static const u8 tsif_settings[2] = { 0x01, 0x01 }; + static const u8 init_settings[14] = { + 0x07, 0x06, 0x01, 0xf0, 0x00, 0x00, 0x04, 0xb0, 0x00, 0x00, + 0x09, 0x9c, 0x0e, 0x4c + }; + static const u8 clk_mode_settings_a1[9] = { + 0x52, 0x49, 0x2c, 0x51, 0x51, 0x3d, 0x15, 0x29, 0x0c + }; + + static const u8 clk_mode_settings_b1[9] = { + 0x5d, 0x55, 0x32, 0x5c, 0x5c, 0x45, 0x17, 0x2e, 0x0d + }; + + static const u8 clk_mode_settings_c1[9] = { + 0x60, 0x00, 0x34, 0x5e, 0x5e, 0x47, 0x18, 0x2f, 0x0e + }; + + static const u8 clk_mode_settings_a2[13] = { + 0x04, 0xe7, 0x94, 0x92, 0x09, 0xcf, 0x7e, 0xd0, 0x49, + 0xcd, 0xcd, 0x1f, 0x5b + }; + + static const u8 clk_mode_settings_b2[13] = { + 0x05, 0x90, 0x27, 0x55, 0x0b, 0x20, 0x8f, 0xd6, 0xea, + 0xc8, 0xc8, 0x23, 0x91 + }; + + static const u8 clk_mode_settings_c2[13] = { + 0x05, 0xb8, 0xd8, 0x00, 0x0b, 0x72, 0x93, 0xf3, 0x00, + 0xcd, 0xcd, 0x24, 0x95 + }; + + static const u8 clk_mode_settings_a3[5] = { + 0x0b, 0x6a, 0xc9, 0x03, 0x33 + }; + static const u8 clk_mode_settings_b3[5] = { + 0x01, 0x02, 0xe4, 0x03, 0x39 + }; + static const u8 clk_mode_settings_c3[5] = { + 0x01, 0x02, 0xeb, 0x03, 0x3b + }; + + static const u8 gtdofst[2] = { 0x3f, 0xff }; + + static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 }; + static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 }; + static const u8 bw8_sst_a[2] = { 0x06, 0x2a }; + static const u8 bw8_sst_b[2] = { 0x06, 0x29 }; + static const u8 bw8_sst_c[2] = { 0x06, 0x28 }; + static const u8 bw8_mrc_a[9] = { + 0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00 + }; + static const u8 bw8_mrc_b[9] = { + 0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55 + }; + static const u8 bw8_mrc_c[9] = { + 0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00 + }; + + static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 }; + static const u8 bw7_sst_a[2] = { 0x06, 0x23 }; + static const u8 bw7_sst_b[2] = { 0x06, 0x22 }; + static const u8 bw7_sst_c[2] = { 0x06, 0x21 }; + static const u8 bw7_mrc_a[9] = { + 0x2d, 0xb6, 0x5b, 0x6d, 0x6d, 0xb6, 0x00, 0xa4, 0x92 + }; + static const u8 bw7_mrc_b[9] = { + 0x33, 0xda, 0x67, 0xb4, 0x7c, 0x71, 0x00, 0xba, 0xaa + }; + static const u8 bw7_mrc_c[9] = { + 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00 + }; + + static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 }; + static const u8 bw6_sst_a[2] = { 0x06, 0x1c }; + static const u8 bw6_sst_b[2] = { 0x06, 0x1b }; + static const u8 bw6_sst_c[2] = { 0x06, 0x1a }; + static const u8 bw6_mrc_a[9] = { + 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00 + }; + static const u8 bw6_mrc_b[9] = { + 0x3c, 0x7e, 0x78, 0xfc, 0x91, 0x2f, 0x00, 0xd9, 0xc7 + }; + static const u8 bw6_mrc_c[9] = { + 0x3e, 0x38, 0x7c, 0x71, 0x95, 0x55, 0x00, 0xdf, 0xff + }; + + static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 }; + static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 }; + static const u8 bw5_sst_a[2] = { 0x06, 0x15 }; + static const u8 bw5_sst_b[2] = { 0x06, 0x15 }; + static const u8 bw5_sst_c[2] = { 0x06, 0x14 }; + static const u8 bw5_mrc_a[9] = { + 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66 + }; + static const u8 bw5_mrc_b[9] = { + 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55 + }; + static const u8 bw5_mrc_c[9] = { + 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc + }; + + static const u8 bw1_7_nomi_a[6] = { + 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03 + }; + static const u8 bw1_7_nomi_c[6] = { + 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03 + }; + static const u8 bw1_7_nomi_b[6] = { + 0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03 + }; + static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c }; + static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c }; + static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b }; + static const u8 bw1_7_mrc_a[9] = { + 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x02, 0xc9, 0x8f + }; + static const u8 bw1_7_mrc_b[9] = { + 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d + }; + static const u8 bw1_7_mrc_c[9] = { + 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x03, 0x40, 0x7d + }; + + const u8 *data = NULL; + const u8 *data2 = NULL; + const u8 *data3 = NULL; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + tune_dmd_setting_seq1, + ARRAY_SIZE(tune_dmd_setting_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq2, + ARRAY_SIZE(tune_dmd_setting_seq2)); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xce, tsif_settings, 2); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8a, init_settings[0]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x90, init_settings[1]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x25); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xf0, &init_settings[2], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2a); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xdc, init_settings[4]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xde, init_settings[5]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2d); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x73, &init_settings[6], 4); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8f, &init_settings[10], 4); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = clk_mode_settings_a1; + data2 = clk_mode_settings_a2; + data3 = clk_mode_settings_a3; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = clk_mode_settings_b1; + data2 = clk_mode_settings_b2; + data3 = clk_mode_settings_b3; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = clk_mode_settings_c1; + data2 = clk_mode_settings_c2; + data3 = clk_mode_settings_c3; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, &data[0], 3); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x22, data[3]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x24, data[4]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x26, data[5]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x29, &data[6], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2d, data[8]); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2e, &data2[0], 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x35, &data2[6], 7); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x3c, &data3[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x56, &data3[2], 3); + if (ret) + return ret; + + switch (bandwidth) { + case CXD2880_DTV_BW_8_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x00); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = gtdofst; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_7_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x02); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_6_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x04); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_5_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x06); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_1_7_MHZ: + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_nomi_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_nomi_c; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x03); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xfd, 0x01); +} + +static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + static const u8 difint_clip[] = { + 0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32 + }; + int ret = 0; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x1d); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x47, difint_clip, 12); + } + + return ret; +} + +static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_profile profile) +{ + u8 t2_mode_tune_mode = 0; + u8 seq_not2_dtime = 0; + u8 dtime1 = 0; + u8 dtime2 = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + switch (tnr_dmd->clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + dtime1 = 0x27; + dtime2 = 0x0c; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + dtime1 = 0x2c; + dtime2 = 0x0d; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + dtime1 = 0x2e; + dtime2 = 0x0e; + break; + default: + return -EINVAL; + } + + switch (profile) { + case CXD2880_DVBT2_PROFILE_BASE: + t2_mode_tune_mode = 0x01; + seq_not2_dtime = dtime2; + break; + + case CXD2880_DVBT2_PROFILE_LITE: + t2_mode_tune_mode = 0x05; + seq_not2_dtime = dtime1; + break; + + case CXD2880_DVBT2_PROFILE_ANY: + t2_mode_tune_mode = 0x00; + seq_not2_dtime = dtime1; + break; + + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2e); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, t2_mode_tune_mode); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2c, seq_not2_dtime); +} + +int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN && + tune_param->profile == CXD2880_DVBT2_PROFILE_ANY) + return -ENOTTY; + + ret = + cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2, + tune_param->center_freq_khz, + tune_param->bandwidth, 0, 0); + if (ret) + return ret; + + ret = + x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth, + tnr_dmd->clk_mode); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub, + tune_param->bandwidth, + tnr_dmd->diver_sub->clk_mode); + if (ret) + return ret; + } + + ret = dvbt2_set_profile(tnr_dmd, tune_param->profile); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile); + if (ret) + return ret; + } + + if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO) + ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0); + else + ret = + cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0, + (u8)(tune_param->data_plp_id)); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + u8 en_fef_intmtnt_ctrl = 1; + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (tune_param->profile) { + case CXD2880_DVBT2_PROFILE_BASE: + en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base; + break; + case CXD2880_DVBT2_PROFILE_LITE: + en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite; + break; + case CXD2880_DVBT2_PROFILE_ANY: + if (tnr_dmd->en_fef_intmtnt_base && + tnr_dmd->en_fef_intmtnt_lite) + en_fef_intmtnt_ctrl = 1; + else + en_fef_intmtnt_ctrl = 0; + break; + default: + return -EINVAL; + } + + ret = + cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, + CXD2880_DTV_SYS_DVBT2, + en_fef_intmtnt_ctrl); + if (ret) + return ret; + + tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2; + tnr_dmd->bandwidth = tune_param->bandwidth; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2; + tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_sleep_dvbt2_demod_setting(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; + } + + if (sync_stat == 6) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return ret; + } + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (ts_lock) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; + } + + if (ts_lock) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return ret; + } else if (!unlock_detected) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + return ret; + } + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd + *tnr_dmd, u8 auto_plp, + u8 plp_id) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x23); + if (ret) + return ret; + + if (!auto_plp) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xaf, plp_id); + if (ret) + return ret; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xad, auto_plp ? 0x00 : 0x01); +} + +int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + struct cxd2880_dvbt2_ofdm ofdm; + static const u8 data[] = { 0, 8, 0, 16, 0, 32, 0, 64, 0, 128, 1, 0}; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) + return 0; + + ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm); + if (ret) + return ret; + + if (!ofdm.mixed) + return 0; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x1d); + if (ret) + return ret; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x47, data, 12); +} + +int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *l1_post_valid) +{ + int ret; + + u8 data; + + if (!tnr_dmd || !l1_post_valid) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &data, 1); + if (ret) + return ret; + + *l1_post_valid = data & 0x01; + + return ret; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h new file mode 100644 index 000000000000..7108e3540093 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt2.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control interface for DVB-T2 + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT2_H +#define CXD2880_TNRDMD_DVBT2_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +enum cxd2880_tnrdmd_dvbt2_tune_info { + CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK, + CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID +}; + +struct cxd2880_dvbt2_tune_param { + u32 center_freq_khz; + enum cxd2880_dtv_bandwidth bandwidth; + u16 data_plp_id; + enum cxd2880_dvbt2_profile profile; + enum cxd2880_tnrdmd_dvbt2_tune_info tune_info; +}; + +#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO 0xffff + +int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd + *tnr_dmd, u8 auto_plp, + u8 plp_id); + +int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *l1_post_valid); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c new file mode 100644 index 000000000000..604580bf7cf7 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c @@ -0,0 +1,1878 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt2_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" + +#include <media/dvb_math.h> + +static const int ref_dbm_1000[4][8] = { + {-96000, -95000, -94000, -93000, -92000, -92000, -98000, -97000}, + {-91000, -89000, -88000, -87000, -86000, -86000, -93000, -92000}, + {-86000, -85000, -83000, -82000, -81000, -80000, -89000, -88000}, + {-82000, -80000, -78000, -76000, -75000, -74000, -86000, -84000}, +}; + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected) +{ + u8 data; + int ret; + + if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, &data, sizeof(data)); + if (ret) + return ret; + + *sync_stat = data & 0x07; + *ts_lock_stat = ((data & 0x20) ? 1 : 0); + *unlock_detected = ((data & 0x10) ? 1 : 0); + + if (*sync_stat == 0x07) + return -EAGAIN; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *sync_stat, + u8 *unlock_detected) +{ + u8 ts_lock_stat = 0; + + if (!tnr_dmd || !sync_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub, + sync_stat, + &ts_lock_stat, + unlock_detected); +} + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset) +{ + u8 data[4]; + u32 ctl_val = 0; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x30, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ctl_val = + ((data[0] & 0x0f) << 24) | (data[1] << 16) | (data[2] << 8) + | (data[3]); + *offset = cxd2880_convert2s_complement(ctl_val, 28); + + switch (tnr_dmd->bandwidth) { + case CXD2880_DTV_BW_1_7_MHZ: + *offset = -1 * ((*offset) / 582); + break; + case CXD2880_DTV_BW_5_MHZ: + case CXD2880_DTV_BW_6_MHZ: + case CXD2880_DTV_BW_7_MHZ: + case CXD2880_DTV_BW_8_MHZ: + *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 940); + break; + default: + return -EINVAL; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset) +{ + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub, + offset); +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_l1pre + *l1_pre) +{ + u8 data[37]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 version = 0; + enum cxd2880_dvbt2_profile profile; + int ret; + + if (!tnr_dmd || !l1_pre) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x61, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + slvt_unfreeze_reg(tnr_dmd); + + l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0]; + l1_pre->bw_ext = data[1] & 0x01; + l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07); + l1_pre->s2 = data[3] & 0x0f; + l1_pre->l1_rep = data[4] & 0x01; + l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07); + l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0f); + l1_pre->mod = + (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0f); + l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03); + l1_pre->fec = + (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03); + l1_pre->l1_post_size = (data[10] & 0x03) << 16; + l1_pre->l1_post_size |= (data[11]) << 8; + l1_pre->l1_post_size |= (data[12]); + l1_pre->l1_post_info_size = (data[13] & 0x03) << 16; + l1_pre->l1_post_info_size |= (data[14]) << 8; + l1_pre->l1_post_info_size |= (data[15]); + l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0f); + l1_pre->tx_id_availability = data[17]; + l1_pre->cell_id = (data[18] << 8); + l1_pre->cell_id |= (data[19]); + l1_pre->network_id = (data[20] << 8); + l1_pre->network_id |= (data[21]); + l1_pre->sys_id = (data[22] << 8); + l1_pre->sys_id |= (data[23]); + l1_pre->num_frames = data[24]; + l1_pre->num_symbols = (data[25] & 0x0f) << 8; + l1_pre->num_symbols |= data[26]; + l1_pre->regen = data[27] & 0x07; + l1_pre->post_ext = data[28] & 0x01; + l1_pre->num_rf_freqs = data[29] & 0x07; + l1_pre->rf_idx = data[30] & 0x07; + version = (data[31] & 0x03) << 2; + version |= (data[32] & 0xc0) >> 6; + l1_pre->t2_version = (enum cxd2880_dvbt2_version)version; + l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5; + l1_pre->t2_base_lite = (data[32] & 0x10) >> 4; + l1_pre->crc32 = (data[33] << 24); + l1_pre->crc32 |= (data[34] << 16); + l1_pre->crc32 |= (data[35] << 8); + l1_pre->crc32 |= data[36]; + + if (profile == CXD2880_DVBT2_PROFILE_BASE) { + switch ((l1_pre->s2 >> 1)) { + case CXD2880_DVBT2_BASE_S2_M1K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M1K; + break; + case CXD2880_DVBT2_BASE_S2_M2K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M2K; + break; + case CXD2880_DVBT2_BASE_S2_M4K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M4K; + break; + case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT: + case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M8K; + break; + case CXD2880_DVBT2_BASE_S2_M16K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M16K; + break; + case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT: + case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M32K; + break; + default: + return -EAGAIN; + } + } else if (profile == CXD2880_DVBT2_PROFILE_LITE) { + switch ((l1_pre->s2 >> 1)) { + case CXD2880_DVBT2_LITE_S2_M2K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M2K; + break; + case CXD2880_DVBT2_LITE_S2_M4K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M4K; + break; + case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT: + case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M8K; + break; + case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT: + case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M16K; + break; + default: + return -EAGAIN; + } + } else { + return -EAGAIN; + } + + l1_pre->mixed = l1_pre->s2 & 0x01; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_version + *ver) +{ + u8 data[2]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 version = 0; + int ret; + + if (!tnr_dmd || !ver) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x80, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + version = ((data[0] & 0x03) << 2); + version |= ((data[1] & 0xc0) >> 6); + *ver = (enum cxd2880_dvbt2_version)version; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_ofdm *ofdm) +{ + u8 data[5]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !ofdm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + + ret = -EAGAIN; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd->diver_sub, + ofdm); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ofdm->mixed = ((data[0] & 0x20) ? 1 : 0); + ofdm->is_miso = ((data[0] & 0x10) >> 4); + ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07); + ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4); + ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07); + ofdm->bw_ext = (data[2] & 0x10) >> 4; + ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0f); + ofdm->num_symbols = (data[3] << 8) | data[4]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd + *tnr_dmd, u8 *plp_ids, + u8 *num_plps) +{ + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !num_plps) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc1, num_plps, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (*num_plps == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EINVAL; + } + + if (!plp_ids) { + slvt_unfreeze_reg(tnr_dmd); + return 0; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc2, + plp_ids, + ((*num_plps > 62) ? + 62 : *num_plps)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (*num_plps > 62) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0c); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, plp_ids + 62, + *num_plps - 62); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_plp + *plp_info) +{ + u8 data[20]; + u8 addr = 0; + u8 index = 0; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !plp_info) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!l1_post_ok) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xa9; + else + addr = 0x96; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + if (type == CXD2880_DVBT2_PLP_COMMON && !data[13]) + return -EAGAIN; + + plp_info->id = data[index++]; + plp_info->type = + (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07); + plp_info->payload = + (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1f); + plp_info->ff = data[index++] & 0x01; + plp_info->first_rf_idx = data[index++] & 0x07; + plp_info->first_frm_idx = data[index++]; + plp_info->group_id = data[index++]; + plp_info->plp_cr = + (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07); + plp_info->constell = + (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07); + plp_info->rot = data[index++] & 0x01; + plp_info->fec = + (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03); + plp_info->num_blocks_max = (data[index++] & 0x03) << 8; + plp_info->num_blocks_max |= data[index++]; + plp_info->frm_int = data[index++]; + plp_info->til_len = data[index++]; + plp_info->til_type = data[index++] & 0x01; + + plp_info->in_band_a_flag = data[index++] & 0x01; + plp_info->rsvd = data[index++] << 8; + plp_info->rsvd |= data[index++]; + + plp_info->in_band_b_flag = + (plp_info->rsvd & 0x8000) >> 15; + plp_info->plp_mode = + (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000c) >> 2); + plp_info->static_flag = (plp_info->rsvd & 0x0002) >> 1; + plp_info->static_padding_flag = plp_info->rsvd & 0x0001; + plp_info->rsvd = (plp_info->rsvd & 0x7ff0) >> 4; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *plp_error) +{ + u8 data; + int ret; + + if (!tnr_dmd || !plp_error) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if ((data & 0x01) == 0x00) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc0, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *plp_error = data & 0x01; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd + *tnr_dmd, u8 *l1_change) +{ + u8 data; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !l1_change) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x5f, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + *l1_change = data & 0x01; + if (*l1_change) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x22); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x16, 0x01); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + slvt_unfreeze_reg(tnr_dmd); + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt2_l1post + *l1_post) +{ + u8 data[16]; + int ret; + + if (!tnr_dmd || !l1_post) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, data, sizeof(data)); + if (ret) + return ret; + + if (!(data[0] & 0x01)) + return -EAGAIN; + + l1_post->sub_slices_per_frame = (data[1] & 0x7f) << 8; + l1_post->sub_slices_per_frame |= data[2]; + l1_post->num_plps = data[3]; + l1_post->num_aux = data[4] & 0x0f; + l1_post->aux_cfg_rfu = data[5]; + l1_post->rf_idx = data[6] & 0x07; + l1_post->freq = data[7] << 24; + l1_post->freq |= data[8] << 16; + l1_post->freq |= data[9] << 8; + l1_post->freq |= data[10]; + l1_post->fef_type = data[11] & 0x0f; + l1_post->fef_length = data[12] << 16; + l1_post->fef_length |= data[13] << 8; + l1_post->fef_length |= data[14]; + l1_post->fef_intvl = data[15]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_bbheader + *bbheader) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 data[14]; + u8 addr = 0; + int ret; + + if (!tnr_dmd || !bbheader) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!ts_lock) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + u8 l1_post_ok; + u8 data; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0x51; + else + addr = 0x42; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + bbheader->stream_input = + (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03); + bbheader->is_single_input_stream = (data[0] >> 5) & 0x01; + bbheader->is_constant_coding_modulation = + (data[0] >> 4) & 0x01; + bbheader->issy_indicator = (data[0] >> 3) & 0x01; + bbheader->null_packet_deletion = (data[0] >> 2) & 0x01; + bbheader->ext = data[0] & 0x03; + + bbheader->input_stream_identifier = data[1]; + bbheader->plp_mode = + (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM : + CXD2880_DVBT2_PLP_MODE_NM; + bbheader->data_field_length = (data[4] << 8) | data[5]; + + if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) { + bbheader->user_packet_length = + (data[6] << 8) | data[7]; + bbheader->sync_byte = data[8]; + bbheader->issy = 0; + } else { + bbheader->user_packet_length = 0; + bbheader->sync_byte = 0; + bbheader->issy = + (data[11] << 16) | (data[12] << 8) | data[13]; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + u32 *ts_rate_bps) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 l1_post_ok = 0; + u8 data[4]; + u8 addr = 0; + + int ret; + + if (!tnr_dmd || !ts_rate_bps) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!ts_lock) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xba; + else + addr = 0xa7; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, &data[0], 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if ((data[0] & 0x80) == 0x00) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x25); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xa6; + else + addr = 0xaa; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, &data[0], 4); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + *ts_rate_bps = ((data[0] & 0x07) << 24) | (data[1] << 16) | + (data[2] << 8) | data[3]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 early_unlock = 0; + u8 data = 0; + int ret; + + if (!tnr_dmd || !sense) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock, + &early_unlock); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + + ret = -EAGAIN; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(tnr_dmd->diver_sub, + sense); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2f, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *sense = + (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV : + CXD2880_TNRDMD_SPECTRUM_NORMAL; + + return 0; +} + +static int dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd, + u16 *reg_value) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 data[2]; + int ret; + + if (!tnr_dmd || !reg_value) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x13, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *reg_value = (data[0] << 8) | data[1]; + + return ret; +} + +static int dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd, + u32 reg_value, int *snr) +{ + if (!tnr_dmd || !snr) + return -EINVAL; + + if (reg_value == 0) + return -EAGAIN; + + if (reg_value > 10876) + reg_value = 10876; + + *snr = intlog10(reg_value) - intlog10(12600 - reg_value); + *snr = (*snr + 839) / 1678 + 32000; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr) +{ + u16 reg_value = 0; + int ret; + + if (!tnr_dmd || !snr) + return -EINVAL; + + *snr = -1000 * 1000; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + ret = dvbt2_read_snr_reg(tnr_dmd, ®_value); + if (ret) + return ret; + + ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr); + } else { + int snr_main = 0; + int snr_sub = 0; + + ret = + cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main, + &snr_sub); + } + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub) +{ + u16 reg_value = 0; + u32 reg_value_sum = 0; + int ret; + + if (!tnr_dmd || !snr || !snr_main || !snr_sub) + return -EINVAL; + + *snr = -1000 * 1000; + *snr_main = -1000 * 1000; + *snr_sub = -1000 * 1000; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = dvbt2_read_snr_reg(tnr_dmd, ®_value); + if (!ret) { + ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, ®_value); + if (!ret) { + ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + return dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr); +} + +int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen) +{ + int ret; + u8 data[3]; + + if (!tnr_dmd || !pen) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x39, data, sizeof(data)); + if (ret) + return ret; + + if (!(data[0] & 0x01)) + return -EAGAIN; + + *pen = ((data[1] << 8) | data[2]); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + u8 ctl_val_reg[5]; + u8 nominal_rate_reg[5]; + u32 trl_ctl_val = 0; + u32 trcg_nominal_rate = 0; + int num; + int den; + int ret; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + s8 diff_upper = 0; + + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x34, ctl_val_reg, + sizeof(ctl_val_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, nominal_rate_reg, + sizeof(nominal_rate_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + diff_upper = + (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f); + + if (diff_upper < -1 || diff_upper > 1) + return -EAGAIN; + + trl_ctl_val = ctl_val_reg[1] << 24; + trl_ctl_val |= ctl_val_reg[2] << 16; + trl_ctl_val |= ctl_val_reg[3] << 8; + trl_ctl_val |= ctl_val_reg[4]; + + trcg_nominal_rate = nominal_rate_reg[1] << 24; + trcg_nominal_rate |= nominal_rate_reg[2] << 16; + trcg_nominal_rate |= nominal_rate_reg[3] << 8; + trcg_nominal_rate |= nominal_rate_reg[4]; + + trl_ctl_val >>= 1; + trcg_nominal_rate >>= 1; + + if (diff_upper == 1) + num = + (int)((trl_ctl_val + 0x80000000u) - + trcg_nominal_rate); + else if (diff_upper == -1) + num = + -(int)((trcg_nominal_rate + 0x80000000u) - + trl_ctl_val); + else + num = (int)(trl_ctl_val - trcg_nominal_rate); + + den = (nominal_rate_reg[0] & 0x7f) << 24; + den |= nominal_rate_reg[1] << 16; + den |= nominal_rate_reg[2] << 8; + den |= nominal_rate_reg[3]; + den = (den + (390625 / 2)) / 390625; + + den >>= 1; + + if (num >= 0) + *ppm = (num + (den / 2)) / den; + else + *ppm = (num - (den / 2)) / den; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm) +{ + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub, + ppm); +} + +int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_plp_btype type, + enum cxd2880_dvbt2_plp_constell *qam) +{ + u8 data; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !qam) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb1, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } else { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9e, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + *qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + enum + cxd2880_dvbt2_plp_code_rate + *code_rate) +{ + u8 data; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !code_rate) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb0, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } else { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9d, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + *code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_profile + *profile) +{ + u8 data; + int ret; + + if (!tnr_dmd || !profile) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x22, &data, sizeof(data)); + if (ret) + return ret; + + if (data & 0x02) { + if (data & 0x01) + *profile = CXD2880_DVBT2_PROFILE_LITE; + else + *profile = CXD2880_DVBT2_PROFILE_BASE; + } else { + ret = -EAGAIN; + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd->diver_sub, + profile); + + return ret; + } + + return 0; +} + +static int dvbt2_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd, + int rf_lvl, u8 *ssi) +{ + enum cxd2880_dvbt2_plp_constell qam; + enum cxd2880_dvbt2_plp_code_rate code_rate; + int prel; + int temp_ssi = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA, + &code_rate); + if (ret) + return ret; + + if (code_rate > CXD2880_DVBT2_R2_5 || qam > CXD2880_DVBT2_QAM256) + return -EINVAL; + + prel = rf_lvl - ref_dbm_1000[qam][code_rate]; + + if (prel < -15000) + temp_ssi = 0; + else if (prel < 0) + temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000; + else if (prel < 20000) + temp_ssi = (((4 * prel) + 500) / 1000) + 10; + else if (prel < 35000) + temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90; + else + temp_ssi = 100; + + *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl); + if (ret) + return ret; + + return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl); + if (ret) + return ret; + + return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h new file mode 100644 index 000000000000..5b7adaceff22 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt2_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT2_MON_H +#define CXD2880_TNRDMD_DVBT2_MON_H + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_dvbt2.h" + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *sync_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset); + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset); + +int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_l1pre + *l1_pre); + +int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_version + *ver); + +int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_ofdm *ofdm); + +int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd + *tnr_dmd, u8 *plp_ids, + u8 *num_plps); + +int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_plp + *plp_info); + +int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *plp_error); + +int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd + *tnr_dmd, u8 *l1_change); + +int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt2_l1post + *l1_post); + +int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_bbheader + *bbheader); + +int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + u32 *ts_rate_bps); + +int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense); + +int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr); + +int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, + int *snr_sub); + +int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen); + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm); + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm); + +int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_plp_btype type, + enum cxd2880_dvbt2_plp_constell + *qam); + +int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + enum + cxd2880_dvbt2_plp_code_rate + *code_rate); + +int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_profile + *profile); + +int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *ssi); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c new file mode 100644 index 000000000000..fedc3b4a2fa0 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" + +#include <media/dvb_math.h> + +static const int ref_dbm_1000[3][5] = { + {-93000, -91000, -90000, -89000, -88000}, + {-87000, -85000, -84000, -83000, -82000}, + {-82000, -80000, -78000, -77000, -76000}, +}; + +static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected) +{ + u8 rdata = 0x00; + int ret; + + if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, &rdata, 1); + if (ret) + return ret; + + *unlock_detected = (rdata & 0x10) ? 1 : 0; + *sync_stat = rdata & 0x07; + *ts_lock_stat = (rdata & 0x20) ? 1 : 0; + + if (*sync_stat == 0x07) + return -EAGAIN; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *unlock_detected) +{ + u8 ts_lock_stat = 0; + + if (!tnr_dmd || !sync_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub, + sync_stat, + &ts_lock_stat, + unlock_detected); +} + +int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt_mode + *mode, + enum cxd2880_dvbt_guard + *guard) +{ + u8 rdata = 0x00; + int ret; + + if (!tnr_dmd || !mode || !guard) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt_mon_mode_guard(tnr_dmd->diver_sub, + mode, guard); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, &rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03); + *guard = (enum cxd2880_dvbt_guard)(rdata & 0x03); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset) +{ + u8 rdata[4]; + u32 ctl_val = 0; + int ret; + + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, rdata, 4); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ctl_val = + ((rdata[0] & 0x1f) << 24) | (rdata[1] << 16) | (rdata[2] << 8) | + (rdata[3]); + *offset = cxd2880_convert2s_complement(ctl_val, 29); + *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 235); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset) +{ + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub, + offset); +} + +int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt_tpsinfo + *info) +{ + u8 rdata[7]; + u8 cell_id_ok = 0; + int ret; + + if (!tnr_dmd || !info) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub, + info); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x29, rdata, 7); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x11); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd5, &cell_id_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + info->constellation = + (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03); + info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07); + info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07); + info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07); + info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03); + info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03); + info->fnum = (rdata[2] >> 6) & 0x03; + info->length_indicator = rdata[2] & 0x3f; + info->cell_id = (rdata[3] << 8) | rdata[4]; + info->reserved_even = rdata[5] & 0x3f; + info->reserved_odd = rdata[6] & 0x3f; + + info->cell_id_ok = cell_id_ok & 0x01; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen) +{ + u8 rdata[3]; + int ret; + + if (!tnr_dmd || !pen) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x26, rdata, 3); + if (ret) + return ret; + + if (!(rdata[0] & 0x01)) + return -EAGAIN; + + *pen = (rdata[1] << 8) | rdata[2]; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd || !sense) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(tnr_dmd->diver_sub, + sense); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1c, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *sense = + (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV : + CXD2880_TNRDMD_SPECTRUM_NORMAL; + + return ret; +} + +static int dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd, + u16 *reg_value) +{ + u8 rdata[2]; + int ret; + + if (!tnr_dmd || !reg_value) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x13, rdata, 2); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *reg_value = (rdata[0] << 8) | rdata[1]; + + return ret; +} + +static int dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd, + u32 reg_value, int *snr) +{ + if (!tnr_dmd || !snr) + return -EINVAL; + + if (reg_value == 0) + return -EAGAIN; + + if (reg_value > 4996) + reg_value = 4996; + + *snr = intlog10(reg_value) - intlog10(5350 - reg_value); + *snr = (*snr + 839) / 1678 + 28500; + + return 0; +} + +int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr) +{ + u16 reg_value = 0; + int ret; + + if (!tnr_dmd || !snr) + return -EINVAL; + + *snr = -1000 * 1000; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + ret = dvbt_read_snr_reg(tnr_dmd, ®_value); + if (ret) + return ret; + + ret = dvbt_calc_snr(tnr_dmd, reg_value, snr); + } else { + int snr_main = 0; + int snr_sub = 0; + + ret = + cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main, + &snr_sub); + } + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub) +{ + u16 reg_value = 0; + u32 reg_value_sum = 0; + int ret; + + if (!tnr_dmd || !snr || !snr_main || !snr_sub) + return -EINVAL; + + *snr = -1000 * 1000; + *snr_main = -1000 * 1000; + *snr_sub = -1000 * 1000; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = dvbt_read_snr_reg(tnr_dmd, ®_value); + if (!ret) { + ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, ®_value); + if (!ret) { + ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + return dvbt_calc_snr(tnr_dmd, reg_value_sum, snr); +} + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + u8 ctl_val_reg[5]; + u8 nominal_rate_reg[5]; + u32 trl_ctl_val = 0; + u32 trcg_nominal_rate = 0; + int num; + int den; + s8 diff_upper = 0; + int ret; + + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x21, ctl_val_reg, + sizeof(ctl_val_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, nominal_rate_reg, + sizeof(nominal_rate_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + diff_upper = + (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f); + + if (diff_upper < -1 || diff_upper > 1) + return -EAGAIN; + + trl_ctl_val = ctl_val_reg[1] << 24; + trl_ctl_val |= ctl_val_reg[2] << 16; + trl_ctl_val |= ctl_val_reg[3] << 8; + trl_ctl_val |= ctl_val_reg[4]; + + trcg_nominal_rate = nominal_rate_reg[1] << 24; + trcg_nominal_rate |= nominal_rate_reg[2] << 16; + trcg_nominal_rate |= nominal_rate_reg[3] << 8; + trcg_nominal_rate |= nominal_rate_reg[4]; + + trl_ctl_val >>= 1; + trcg_nominal_rate >>= 1; + + if (diff_upper == 1) + num = + (int)((trl_ctl_val + 0x80000000u) - + trcg_nominal_rate); + else if (diff_upper == -1) + num = + -(int)((trcg_nominal_rate + 0x80000000u) - + trl_ctl_val); + else + num = (int)(trl_ctl_val - trcg_nominal_rate); + + den = (nominal_rate_reg[0] & 0x7f) << 24; + den |= nominal_rate_reg[1] << 16; + den |= nominal_rate_reg[2] << 8; + den |= nominal_rate_reg[3]; + den = (den + (390625 / 2)) / 390625; + + den >>= 1; + + if (num >= 0) + *ppm = (num + (den / 2)) / den; + else + *ppm = (num - (den / 2)) / den; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm); +} + +static int dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd, + int rf_lvl, u8 *ssi) +{ + struct cxd2880_dvbt_tpsinfo tps; + int prel; + int temp_ssi = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps); + if (ret) + return ret; + + if (tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3 || + tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5) + return -EINVAL; + + prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp]; + + if (prel < -15000) + temp_ssi = 0; + else if (prel < 0) + temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000; + else if (prel < 20000) + temp_ssi = (((4 * prel) + 500) / 1000) + 10; + else if (prel < 35000) + temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90; + else + temp_ssi = 100; + + *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl); + if (ret) + return ret; + + return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl); + if (ret) + return ret; + + return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 sync = 0; + u8 tslock = 0; + u8 early_unlock = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock, + &early_unlock); + if (ret) + return ret; + + if (sync != 6) + return -EAGAIN; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h new file mode 100644 index 000000000000..f4c31725fa48 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT_MON_H +#define CXD2880_TNRDMD_DVBT_MON_H + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_dvbt.h" + +int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt_mode + *mode, + enum cxd2880_dvbt_guard + *guard); + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset); + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset); + +int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt_tpsinfo + *info); + +int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen); + +int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense); + +int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr); + +int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub); + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm); + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm); + +int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c new file mode 100644 index 000000000000..3d8012c18e3f --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd_mon.h" + +static const u8 rf_lvl_seq[2] = { + 0x80, 0x00, +}; + +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db) +{ + u8 rdata[2]; + int ret; + + if (!tnr_dmd || !rf_lvl_db) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, 0x01); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x5b, rf_lvl_seq, 2); + if (ret) + return ret; + + usleep_range(2000, 3000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, rdata, 2); + if (ret) + return ret; + + if (rdata[0] || rdata[1]) + return -EINVAL; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, rdata, 2); + if (ret) + return ret; + + *rf_lvl_db = + cxd2880_convert2s_complement((rdata[0] << 3) | + ((rdata[1] & 0xe0) >> 5), 11); + + *rf_lvl_db *= 125; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, 0x00); + if (ret) + return ret; + + if (tnr_dmd->rf_lvl_cmpstn) + ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db); + + return ret; +} + +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db) +{ + if (!tnr_dmd || !rf_lvl_db) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db); +} + +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, u16 *status) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd || !status) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, data, 2); + if (ret) + return ret; + + *status = (data[0] << 8) | data[1]; + + return 0; +} + +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + u16 *status) +{ + if (!tnr_dmd || !status) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, + status); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h new file mode 100644 index 000000000000..570360925f87 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_MON_H +#define CXD2880_TNRDMD_MON_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, u16 *status); + +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + u16 *status); +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c new file mode 100644 index 000000000000..d474dc1b05da --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -0,0 +1,1947 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_top.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/spi/spi.h> + +#include <media/dvb_frontend.h> +#include <media/dvb_math.h> + +#include "cxd2880.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" +#include "cxd2880_integ.h" +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_devio_spi.h" +#include "cxd2880_spi_device.h" +#include "cxd2880_tnrdmd_driver_version.h" + +struct cxd2880_priv { + struct cxd2880_tnrdmd tnrdmd; + struct spi_device *spi; + struct cxd2880_io regio; + struct cxd2880_spi_device spi_device; + struct cxd2880_spi cxd2880_spi; + struct cxd2880_dvbt_tune_param dvbt_tune_param; + struct cxd2880_dvbt2_tune_param dvbt2_tune_param; + struct mutex *spi_mutex; /* For SPI access exclusive control */ + unsigned long pre_ber_update; + unsigned long pre_ber_interval; + unsigned long post_ber_update; + unsigned long post_ber_interval; + unsigned long ucblock_update; + unsigned long ucblock_interval; + enum fe_status s; +}; + +static int cxd2880_pre_bit_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *pre_bit_err, u32 *pre_bit_count) +{ + u8 rdata[2]; + int ret; + + if (!tnrdmd || !pre_bit_err || !pre_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x39, rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if ((rdata[0] & 0x01) == 0) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x22, rdata, 2); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + *pre_bit_err = (rdata[0] << 8) | rdata[1]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x6f, rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + slvt_unfreeze_reg(tnrdmd); + + *pre_bit_count = ((rdata[0] & 0x07) == 0) ? + 256 : (0x1000 << (rdata[0] & 0x07)); + + return 0; +} + +static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *pre_bit_err, + u32 *pre_bit_count) +{ + u32 period_exp = 0; + u32 n_ldpc = 0; + u8 data[5]; + int ret; + + if (!tnrdmd || !pre_bit_err || !pre_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x3c, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (!(data[0] & 0x01)) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + *pre_bit_err = + ((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xa0, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) == + CXD2880_DVBT2_FEC_LDPC_16K) + n_ldpc = 16200; + else + n_ldpc = 64800; + slvt_unfreeze_reg(tnrdmd); + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x6f, data, 1); + if (ret) + return ret; + + period_exp = data[0] & 0x0f; + + *pre_bit_count = (1U << period_exp) * n_ldpc; + + return 0; +} + +static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *post_bit_err, + u32 *post_bit_count) +{ + u8 rdata[3]; + u32 bit_error = 0; + u32 period_exp = 0; + int ret; + + if (!tnrdmd || !post_bit_err || !post_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x15, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x40) == 0) + return -EAGAIN; + + *post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x60, rdata, 1); + if (ret) + return ret; + + period_exp = (rdata[0] & 0x1f); + + if (period_exp <= 11 && (bit_error > (1U << period_exp) * 204 * 8)) + return -EAGAIN; + + *post_bit_count = (1U << period_exp) * 204 * 8; + + return 0; +} + +static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *post_bit_err, + u32 *post_bit_count) +{ + u32 period_exp = 0; + u32 n_bch = 0; + u8 data[3]; + enum cxd2880_dvbt2_plp_fec plp_fec_type = + CXD2880_DVBT2_FEC_LDPC_16K; + enum cxd2880_dvbt2_plp_code_rate plp_code_rate = + CXD2880_DVBT2_R1_2; + int ret; + static const u16 n_bch_bits_lookup[2][8] = { + {7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480}, + {32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920} + }; + + if (!tnrdmd || !post_bit_err || !post_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x15, data, 3); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (!(data[0] & 0x40)) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + + *post_bit_err = + ((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x9d, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + plp_code_rate = + (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07); + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xa0, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03); + + slvt_unfreeze_reg(tnrdmd); + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x72, data, 1); + if (ret) + return ret; + + period_exp = data[0] & 0x0f; + + if (plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K || + plp_code_rate > CXD2880_DVBT2_R2_5) + return -EAGAIN; + + n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate]; + + if (*post_bit_err > ((1U << period_exp) * n_bch)) + return -EAGAIN; + + *post_bit_count = (1U << period_exp) * n_bch; + + return 0; +} + +static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *block_err, + u32 *block_count) +{ + u8 rdata[3]; + int ret; + + if (!tnrdmd || !block_err || !block_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x18, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x01) == 0) + return -EAGAIN; + + *block_err = (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x5c, rdata, 1); + if (ret) + return ret; + + *block_count = 1U << (rdata[0] & 0x0f); + + if ((*block_count == 0) || (*block_err > *block_count)) + return -EAGAIN; + + return 0; +} + +static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *block_err, + u32 *block_count) +{ + u8 rdata[3]; + int ret; + + if (!tnrdmd || !block_err || !block_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x18, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x01) == 0) + return -EAGAIN; + + *block_err = (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x24); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xdc, rdata, 1); + if (ret) + return ret; + + *block_count = 1U << (rdata[0] & 0x0f); + + if ((*block_count == 0) || (*block_err > *block_count)) + return -EAGAIN; + + return 0; +} + +static void cxd2880_release(struct dvb_frontend *fe) +{ + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg.\n"); + return; + } + priv = fe->demodulator_priv; + kfree(priv); +} + +static int cxd2880_init(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct cxd2880_tnrdmd_create_param create_param; + + if (!fe) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI; + create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE; + create_param.en_internal_ldo = 1; + create_param.xosc_cap = 18; + create_param.xosc_i = 8; + create_param.stationary_use = 1; + + mutex_lock(priv->spi_mutex); + if (priv->tnrdmd.io != &priv->regio) { + ret = cxd2880_tnrdmd_create(&priv->tnrdmd, + &priv->regio, &create_param); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_info("cxd2880 tnrdmd create failed %d\n", ret); + return ret; + } + } + ret = cxd2880_integ_init(&priv->tnrdmd); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_err("cxd2880 integ init failed %d\n", ret); + return ret; + } + mutex_unlock(priv->spi_mutex); + + pr_debug("OK.\n"); + + return ret; +} + +static int cxd2880_sleep(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd); + mutex_unlock(priv->spi_mutex); + + pr_debug("tnrdmd_sleep ret %d\n", ret); + + return ret; +} + +static int cxd2880_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + int level = 0; + + if (!fe || !strength) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT || + c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level); + } else { + pr_debug("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + level /= 125; + /* + * level should be between -105dBm and -30dBm. + * E.g. they should be between: + * -105000/125 = -840 and -30000/125 = -240 + */ + level = clamp(level, -840, -240); + /* scale value to 0x0000-0xffff */ + *strength = ((level + 840) * 0xffff) / (-240 + 840); + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + int ret; + int snrvalue = 0; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !snr) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd, + &snrvalue); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd, + &snrvalue); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + if (snrvalue < 0) + snrvalue = 0; + *snr = snrvalue; + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !ucblocks) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd, + ucblocks); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd, + ucblocks); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + +static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv; + struct cxd2880_dvbt_tpsinfo info; + enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + u32 pre_ber_rate = 0; + u32 post_ber_rate = 0; + u32 ucblock_rate = 0; + u32 mes_exp = 0; + static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125}; + static const int denominator_tbl[4] = {125664, 129472, 137088, 152320}; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + bw = priv->dvbt_tune_param.bandwidth; + + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, + &info); + if (ret) { + pr_err("tps monitor error ret = %d\n", ret); + info.hierarchy = CXD2880_DVBT_HIERARCHY_NON; + info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK; + info.guard = CXD2880_DVBT_GUARD_1_4; + info.rate_hp = CXD2880_DVBT_CODERATE_1_2; + info.rate_lp = CXD2880_DVBT_CODERATE_1_2; + } + + if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) { + pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + ucblock_rate = 875 * cr_table[info.rate_hp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + } else { + u8 data = 0; + struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (!ret) { + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x67, &data, 1); + if (ret) + data = 0x00; + } else { + data = 0x00; + } + + if (data & 0x01) { /* Low priority */ + pre_ber_rate = + 63000000 * bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_lp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_lp] * + bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + } else { /* High priority */ + pre_ber_rate = + 63000000 * bw * 2 / denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 / + denominator_tbl[info.guard]; + + ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] * + bw * 2 / denominator_tbl[info.guard]; + } + } + + mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24; + priv->pre_ber_interval = + ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) / + pre_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD, + mes_exp == 8 ? 0 : mes_exp - 12); + + mes_exp = intlog2(post_ber_rate) >> 24; + priv->post_ber_interval = + ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) / + post_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD, + mes_exp); + + mes_exp = intlog2(ucblock_rate) >> 24; + priv->ucblock_interval = + ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) / + ucblock_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_PER_MES, + mes_exp); + + return 0; +} + +static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv; + struct cxd2880_dvbt2_l1pre l1pre; + struct cxd2880_dvbt2_l1post l1post; + struct cxd2880_dvbt2_plp plp; + struct cxd2880_dvbt2_bbheader bbheader; + enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + u32 pre_ber_rate = 0; + u32 post_ber_rate = 0; + u32 ucblock_rate = 0; + u32 mes_exp = 0; + u32 term_a = 0; + u32 term_b = 0; + u32 denominator = 0; + static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76}; + static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1}; + static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32}; + static const u32 kbch_tbl[2][8] = { + {6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232}, + {32128, 38608, 42960, 48328, 51568, 53760, 0, 0} + }; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + bw = priv->dvbt2_tune_param.bandwidth; + + ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre); + if (ret) { + pr_info("l1 pre error\n"); + goto error_ber_setting; + } + + ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, &plp); + if (ret) { + pr_info("plp info error\n"); + goto error_ber_setting; + } + + ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post); + if (ret) { + pr_info("l1 post error\n"); + goto error_ber_setting; + } + + term_a = + (mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) * + (l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048; + + if (l1pre.mixed && l1post.fef_intvl) { + term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) / + l1post.fef_intvl; + } else { + term_b = 0; + } + + switch (bw) { + case CXD2880_DTV_BW_1_7_MHZ: + denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131; + break; + case CXD2880_DTV_BW_5_MHZ: + denominator = ((term_a + term_b) * 7 + 20) / 40; + break; + case CXD2880_DTV_BW_6_MHZ: + denominator = ((term_a + term_b) * 7 + 24) / 48; + break; + case CXD2880_DTV_BW_7_MHZ: + denominator = ((term_a + term_b) + 4) / 8; + break; + case CXD2880_DTV_BW_8_MHZ: + default: + denominator = ((term_a + term_b) * 7 + 32) / 64; + break; + } + + if (plp.til_type && plp.til_len) { + pre_ber_rate = + (plp.num_blocks_max * 1000000 + (denominator / 2)) / + denominator; + pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) / + plp.til_len; + } else { + pre_ber_rate = + (plp.num_blocks_max * 1000000 + (denominator / 2)) / + denominator; + } + + post_ber_rate = pre_ber_rate; + + mes_exp = intlog2(pre_ber_rate) >> 24; + priv->pre_ber_interval = + ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) / + pre_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, + mes_exp); + + mes_exp = intlog2(post_ber_rate) >> 24; + priv->post_ber_interval = + ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) / + post_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, + mes_exp); + + ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &bbheader); + if (ret) { + pr_info("bb header error\n"); + goto error_ucblock_setting; + } + + if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) { + if (!bbheader.issy_indicator) { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + + 752) / 1504; + } else { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + + 764) / 1528; + } + } else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) / + 1496; + } else { + pr_info("plp mode is not Normal or HEM\n"); + goto error_ucblock_setting; + } + + mes_exp = intlog2(ucblock_rate) >> 24; + priv->ucblock_interval = + ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) / + ucblock_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, + mes_exp); + + return 0; + +error_ber_setting: + priv->pre_ber_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0); + + priv->post_ber_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0); + +error_ucblock_setting: + priv->ucblock_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8); + + return 0; +} + +static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 0); + + if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) { + return -ENOTTY; + } + + ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param); + if (ret) + return ret; + + usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000, + CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000); + + return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param); +} + +static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 0); + + if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) { + return -ENOTTY; + } + + if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE && + tune_param->profile != CXD2880_DVBT2_PROFILE_LITE) + return -EINVAL; + + ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param); + if (ret) + return ret; + + usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000, + CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000); + + return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param); +} + +static int cxd2880_set_frontend(struct dvb_frontend *fe) +{ + int ret; + struct dtv_frontend_properties *c; + struct cxd2880_priv *priv; + enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[0].uvalue = 0; + c->pre_bit_error.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].uvalue = 0; + c->pre_bit_count.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_error.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].uvalue = 0; + c->post_bit_count.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].uvalue = 0; + c->block_error.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].uvalue = 0; + c->block_count.len = 1; + + switch (c->bandwidth_hz) { + case 1712000: + bw = CXD2880_DTV_BW_1_7_MHZ; + break; + case 5000000: + bw = CXD2880_DTV_BW_5_MHZ; + break; + case 6000000: + bw = CXD2880_DTV_BW_6_MHZ; + break; + case 7000000: + bw = CXD2880_DTV_BW_7_MHZ; + break; + case 8000000: + bw = CXD2880_DTV_BW_8_MHZ; + break; + default: + return -EINVAL; + } + + priv->s = 0; + + pr_info("sys:%d freq:%d bw:%d\n", + c->delivery_system, c->frequency, bw); + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT; + priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000; + priv->dvbt_tune_param.bandwidth = bw; + priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP; + ret = cxd2880_dvbt_tune(&priv->tnrdmd, + &priv->dvbt_tune_param); + } else if (c->delivery_system == SYS_DVBT2) { + priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2; + priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000; + priv->dvbt2_tune_param.bandwidth = bw; + priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id; + priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE; + ret = cxd2880_dvbt2_tune(&priv->tnrdmd, + &priv->dvbt2_tune_param); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + pr_info("tune result %d\n", ret); + + return ret; +} + +static int cxd2880_get_stats(struct dvb_frontend *fe, + enum fe_status status) +{ + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + u32 pre_bit_err = 0, pre_bit_count = 0; + u32 post_bit_err = 0, post_bit_count = 0; + u32 block_err = 0, block_count = 0; + int ret; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + if (!(status & FE_HAS_LOCK)) { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.len = 1; + c->pre_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; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; + } + + if (time_after(jiffies, priv->pre_ber_update)) { + priv->pre_ber_update = + jiffies + msecs_to_jiffies(priv->pre_ber_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, + &pre_bit_err, + &pre_bit_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd, + &pre_bit_err, + &pre_bit_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + + if (!ret) { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue += pre_bit_err; + c->pre_bit_count.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue += pre_bit_count; + } else { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("pre_bit_error_t failed %d\n", ret); + } + } + + if (time_after(jiffies, priv->post_ber_update)) { + priv->post_ber_update = + jiffies + msecs_to_jiffies(priv->post_ber_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_post_bit_err_t(&priv->tnrdmd, + &post_bit_err, + &post_bit_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_post_bit_err_t2(&priv->tnrdmd, + &post_bit_err, + &post_bit_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + + if (!ret) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += post_bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += post_bit_count; + } else { + c->post_bit_error.len = 1; + 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; + pr_debug("post_bit_err_t %d\n", ret); + } + } + + if (time_after(jiffies, priv->ucblock_update)) { + priv->ucblock_update = + jiffies + msecs_to_jiffies(priv->ucblock_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_read_block_err_t(&priv->tnrdmd, + &block_err, + &block_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_read_block_err_t2(&priv->tnrdmd, + &block_err, + &block_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + if (!ret) { + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += block_err; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += block_count; + } else { + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_block_err_t %d\n", ret); + } + } + + return 0; +} + +static int cxd2880_check_l1post_plp(struct dvb_frontend *fe) +{ + u8 valid = 0; + u8 plp_not_found; + int ret; + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd, + &valid); + if (ret) + return ret; + + if (!valid) + return -EAGAIN; + + ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd, + &plp_not_found); + if (ret) + return ret; + + if (plp_not_found) { + priv->dvbt2_tune_param.tune_info = + CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID; + } else { + priv->dvbt2_tune_param.tune_info = + CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK; + } + + return 0; +} + +static int cxd2880_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + int ret; + u8 sync = 0; + u8 lock = 0; + u8 unlock = 0; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !status) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + *status = 0; + + if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) { + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(&priv->tnrdmd, + &sync, + &lock, + &unlock); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(&priv->tnrdmd, + &sync, + &lock, + &unlock); + } else { + pr_err("invalid system"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + + mutex_unlock(priv->spi_mutex); + if (ret) { + pr_err("failed. sys = %d\n", priv->tnrdmd.sys); + return ret; + } + + if (sync == 6) { + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER; + } + if (lock) + *status |= FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + + pr_debug("status %d\n", *status); + + if (priv->s == 0 && (*status & FE_HAS_LOCK)) { + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_set_ber_per_period_t(fe); + priv->s = *status; + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_check_l1post_plp(fe); + if (!ret) { + ret = cxd2880_set_ber_per_period_t2(fe); + priv->s = *status; + } + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + } + + cxd2880_get_stats(fe, *status); + return 0; +} + +static int cxd2880_tune(struct dvb_frontend *fe, + bool retune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret; + + if (!fe || !delay || !status) { + pr_err("invalid arg."); + return -EINVAL; + } + + if (retune) { + ret = cxd2880_set_frontend(fe); + if (ret) { + pr_err("cxd2880_set_frontend failed %d\n", ret); + return ret; + } + } + + *delay = HZ / 5; + + return cxd2880_read_status(fe, status); +} + +static int cxd2880_get_frontend_t(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + int ret; + struct cxd2880_priv *priv = NULL; + enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K; + enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32; + struct cxd2880_dvbt_tpsinfo tps; + enum cxd2880_tnrdmd_spectrum_sense sense; + u16 snr = 0; + int strength = 0; + + if (!fe || !c) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd, + &mode, &guard); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (mode) { + case CXD2880_DVBT_MODE_2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case CXD2880_DVBT_MODE_8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + c->transmission_mode = TRANSMISSION_MODE_2K; + pr_debug("transmission mode is invalid %d\n", mode); + break; + } + switch (guard) { + case CXD2880_DVBT_GUARD_1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case CXD2880_DVBT_GUARD_1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case CXD2880_DVBT_GUARD_1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case CXD2880_DVBT_GUARD_1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("guard interval is invalid %d\n", + guard); + break; + } + } else { + c->transmission_mode = TRANSMISSION_MODE_2K; + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("ModeGuard err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (tps.hierarchy) { + case CXD2880_DVBT_HIERARCHY_NON: + c->hierarchy = HIERARCHY_NONE; + break; + case CXD2880_DVBT_HIERARCHY_1: + c->hierarchy = HIERARCHY_1; + break; + case CXD2880_DVBT_HIERARCHY_2: + c->hierarchy = HIERARCHY_2; + break; + case CXD2880_DVBT_HIERARCHY_4: + c->hierarchy = HIERARCHY_4; + break; + default: + c->hierarchy = HIERARCHY_NONE; + pr_debug("TPSInfo hierarchy is invalid %d\n", + tps.hierarchy); + break; + } + + switch (tps.rate_hp) { + case CXD2880_DVBT_CODERATE_1_2: + c->code_rate_HP = FEC_1_2; + break; + case CXD2880_DVBT_CODERATE_2_3: + c->code_rate_HP = FEC_2_3; + break; + case CXD2880_DVBT_CODERATE_3_4: + c->code_rate_HP = FEC_3_4; + break; + case CXD2880_DVBT_CODERATE_5_6: + c->code_rate_HP = FEC_5_6; + break; + case CXD2880_DVBT_CODERATE_7_8: + c->code_rate_HP = FEC_7_8; + break; + default: + c->code_rate_HP = FEC_NONE; + pr_debug("TPSInfo rateHP is invalid %d\n", + tps.rate_hp); + break; + } + switch (tps.rate_lp) { + case CXD2880_DVBT_CODERATE_1_2: + c->code_rate_LP = FEC_1_2; + break; + case CXD2880_DVBT_CODERATE_2_3: + c->code_rate_LP = FEC_2_3; + break; + case CXD2880_DVBT_CODERATE_3_4: + c->code_rate_LP = FEC_3_4; + break; + case CXD2880_DVBT_CODERATE_5_6: + c->code_rate_LP = FEC_5_6; + break; + case CXD2880_DVBT_CODERATE_7_8: + c->code_rate_LP = FEC_7_8; + break; + default: + c->code_rate_LP = FEC_NONE; + pr_debug("TPSInfo rateLP is invalid %d\n", + tps.rate_lp); + break; + } + switch (tps.constellation) { + case CXD2880_DVBT_CONSTELLATION_QPSK: + c->modulation = QPSK; + break; + case CXD2880_DVBT_CONSTELLATION_16QAM: + c->modulation = QAM_16; + break; + case CXD2880_DVBT_CONSTELLATION_64QAM: + c->modulation = QAM_64; + break; + default: + c->modulation = QPSK; + pr_debug("TPSInfo constellation is invalid %d\n", + tps.constellation); + break; + } + } else { + c->hierarchy = HIERARCHY_NONE; + c->code_rate_HP = FEC_NONE; + c->code_rate_LP = FEC_NONE; + c->modulation = QPSK; + pr_debug("TPS info err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (sense) { + case CXD2880_TNRDMD_SPECTRUM_NORMAL: + c->inversion = INVERSION_OFF; + break; + case CXD2880_TNRDMD_SPECTRUM_INV: + c->inversion = INVERSION_ON; + break; + default: + c->inversion = INVERSION_OFF; + pr_debug("spectrum sense is invalid %d\n", sense); + break; + } + } else { + c->inversion = INVERSION_OFF; + pr_debug("spectrum_sense %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength); + mutex_unlock(priv->spi_mutex); + if (!ret) { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("mon_rf_lvl %d\n", ret); + } + + ret = cxd2880_read_snr(fe, &snr); + if (!ret) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = snr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_snr %d\n", ret); + } + + return 0; +} + +static int cxd2880_get_frontend_t2(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct cxd2880_dvbt2_l1pre l1pre; + enum cxd2880_dvbt2_plp_code_rate coderate; + enum cxd2880_dvbt2_plp_constell qam; + enum cxd2880_tnrdmd_spectrum_sense sense; + u16 snr = 0; + int strength = 0; + + if (!fe || !c) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (l1pre.fft_mode) { + case CXD2880_DVBT2_M2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case CXD2880_DVBT2_M8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + case CXD2880_DVBT2_M4K: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case CXD2880_DVBT2_M1K: + c->transmission_mode = TRANSMISSION_MODE_1K; + break; + case CXD2880_DVBT2_M16K: + c->transmission_mode = TRANSMISSION_MODE_16K; + break; + case CXD2880_DVBT2_M32K: + c->transmission_mode = TRANSMISSION_MODE_32K; + break; + default: + c->transmission_mode = TRANSMISSION_MODE_2K; + pr_debug("L1Pre fft_mode is invalid %d\n", + l1pre.fft_mode); + break; + } + switch (l1pre.gi) { + case CXD2880_DVBT2_G1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case CXD2880_DVBT2_G1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case CXD2880_DVBT2_G1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case CXD2880_DVBT2_G1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case CXD2880_DVBT2_G1_128: + c->guard_interval = GUARD_INTERVAL_1_128; + break; + case CXD2880_DVBT2_G19_128: + c->guard_interval = GUARD_INTERVAL_19_128; + break; + case CXD2880_DVBT2_G19_256: + c->guard_interval = GUARD_INTERVAL_19_256; + break; + default: + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("L1Pre guard interval is invalid %d\n", + l1pre.gi); + break; + } + } else { + c->transmission_mode = TRANSMISSION_MODE_2K; + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("L1Pre err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &coderate); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (coderate) { + case CXD2880_DVBT2_R1_2: + c->fec_inner = FEC_1_2; + break; + case CXD2880_DVBT2_R3_5: + c->fec_inner = FEC_3_5; + break; + case CXD2880_DVBT2_R2_3: + c->fec_inner = FEC_2_3; + break; + case CXD2880_DVBT2_R3_4: + c->fec_inner = FEC_3_4; + break; + case CXD2880_DVBT2_R4_5: + c->fec_inner = FEC_4_5; + break; + case CXD2880_DVBT2_R5_6: + c->fec_inner = FEC_5_6; + break; + default: + c->fec_inner = FEC_NONE; + pr_debug("CodeRate is invalid %d\n", coderate); + break; + } + } else { + c->fec_inner = FEC_NONE; + pr_debug("CodeRate %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &qam); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (qam) { + case CXD2880_DVBT2_QPSK: + c->modulation = QPSK; + break; + case CXD2880_DVBT2_QAM16: + c->modulation = QAM_16; + break; + case CXD2880_DVBT2_QAM64: + c->modulation = QAM_64; + break; + case CXD2880_DVBT2_QAM256: + c->modulation = QAM_256; + break; + default: + c->modulation = QPSK; + pr_debug("QAM is invalid %d\n", qam); + break; + } + } else { + c->modulation = QPSK; + pr_debug("QAM %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (sense) { + case CXD2880_TNRDMD_SPECTRUM_NORMAL: + c->inversion = INVERSION_OFF; + break; + case CXD2880_TNRDMD_SPECTRUM_INV: + c->inversion = INVERSION_ON; + break; + default: + c->inversion = INVERSION_OFF; + pr_debug("spectrum sense is invalid %d\n", sense); + break; + } + } else { + c->inversion = INVERSION_OFF; + pr_debug("SpectrumSense %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength); + mutex_unlock(priv->spi_mutex); + if (!ret) { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("mon_rf_lvl %d\n", ret); + } + + ret = cxd2880_read_snr(fe, &snr); + if (!ret) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = snr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_snr %d\n", ret); + } + + return 0; +} + +static int cxd2880_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *props) +{ + int ret; + + if (!fe || !props) { + pr_err("invalid arg."); + return -EINVAL; + } + + pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system); + switch (fe->dtv_property_cache.delivery_system) { + case SYS_DVBT: + ret = cxd2880_get_frontend_t(fe, props); + break; + case SYS_DVBT2: + ret = cxd2880_get_frontend_t2(fe, props); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = { + .info = { + .name = "Sony CXD2880", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + 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_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS, + }, + .delsys = { SYS_DVBT, SYS_DVBT2 }, + + .release = cxd2880_release, + .init = cxd2880_init, + .sleep = cxd2880_sleep, + .tune = cxd2880_tune, + .set_frontend = cxd2880_set_frontend, + .get_frontend = cxd2880_get_frontend, + .read_status = cxd2880_read_status, + .read_ber = cxd2880_read_ber, + .read_signal_strength = cxd2880_read_signal_strength, + .read_snr = cxd2880_read_snr, + .read_ucblocks = cxd2880_read_ucblocks, + .get_frontend_algo = cxd2880_get_frontend_algo, +}; + +struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg) +{ + int ret; + enum cxd2880_tnrdmd_chip_id chipid = + CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + static struct cxd2880_priv *priv; + u8 data = 0; + + if (!fe) { + pr_err("invalid arg.\n"); + return NULL; + } + + priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->spi = cfg->spi; + priv->spi_mutex = cfg->spi_mutex; + priv->spi_device.spi = cfg->spi; + + memcpy(&fe->ops, &cxd2880_dvbt_t2_ops, + sizeof(struct dvb_frontend_ops)); + + ret = cxd2880_spi_device_initialize(&priv->spi_device, + CXD2880_SPI_MODE_0, + 55000000); + if (ret) { + pr_err("spi_device_initialize failed. %d\n", ret); + kfree(priv); + return NULL; + } + + ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi, + &priv->spi_device); + if (ret) { + pr_err("spi_device_create_spi failed. %d\n", ret); + kfree(priv); + return NULL; + } + + ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0); + if (ret) { + pr_err("io_spi_create failed. %d\n", ret); + kfree(priv); + return NULL; + } + ret = priv->regio.write_reg(&priv->regio, + CXD2880_IO_TGT_SYS, 0x00, 0x00); + if (ret) { + pr_err("set bank to 0x00 failed.\n"); + kfree(priv); + return NULL; + } + ret = priv->regio.read_regs(&priv->regio, + CXD2880_IO_TGT_SYS, 0xfd, &data, 1); + if (ret) { + pr_err("read chip id failed.\n"); + kfree(priv); + return NULL; + } + + chipid = (enum cxd2880_tnrdmd_chip_id)data; + if (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X && + chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11) { + pr_err("chip id invalid.\n"); + kfree(priv); + return NULL; + } + + fe->demodulator_priv = priv; + pr_info("CXD2880 driver version: Ver %s\n", + CXD2880_TNRDMD_DRIVER_VERSION); + + return fe; +} +EXPORT_SYMBOL(cxd2880_attach); + +MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver"); +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index 64f49c8eb1fb..ee7af34979ed 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -1285,7 +1285,7 @@ int dib0090_gain_control(struct dvb_frontend *fe) #endif if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */ - if (ABS(adc_error) < 50 || state->agc_step++ > 5) { + if (abs(adc_error) < 50 || state->agc_step++ > 5) { #ifdef CONFIG_STANDARD_DAB if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) { @@ -1754,7 +1754,7 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front *tune_state = CT_TUNER_STEP_1; } else { /* the minimum was what we have seen in the step before */ - if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { + if (abs(state->adc_diff) > abs(state->min_adc_diff)) { dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step\n", state->adc_diff, state->min_adc_diff); state->step--; } diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 90ace707a80d..902af482448e 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -809,7 +809,7 @@ static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) { u32 internal = dib7000p_get_internal_freq(state); s32 unit_khz_dds_val; - u32 abs_offset_khz = ABS(offset_khz); + u32 abs_offset_khz = abs(offset_khz); u32 dds = state->cfg.bw->ifreq & 0x1ffffff; u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); if (internal == 0) { diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index e64014f338fb..6f35173d2968 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2677,7 +2677,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff) static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) { s16 unit_khz_dds_val; - u32 abs_offset_khz = ABS(offset_khz); + u32 abs_offset_khz = abs(offset_khz); u32 dds = state->cfg.pll->ifreq & 0x1ffffff; u8 invert = !!(state->cfg.pll->ifreq & (1 << 25)); u8 ratio; diff --git a/drivers/media/dvb-frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c index d981233e458f..70119c79ac2b 100644 --- a/drivers/media/dvb-frontends/dibx000_common.c +++ b/drivers/media/dvb-frontends/dibx000_common.c @@ -424,7 +424,7 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char *name, struct dibx000_i2c_master *mst) { - strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); + strlcpy(i2c_adap->name, name, sizeof(i2c_adap->name)); i2c_adap->algo = algo; i2c_adap->algo_data = NULL; i2c_set_adapdata(i2c_adap, mst); diff --git a/drivers/media/dvb-frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h index 8784af962eba..12b58f5c677d 100644 --- a/drivers/media/dvb-frontends/dibx000_common.h +++ b/drivers/media/dvb-frontends/dibx000_common.h @@ -223,8 +223,6 @@ struct dvb_frontend_parametersContext { #define FE_CALLBACK_TIME_NEVER 0xffffffff -#define ABS(x) ((x < 0) ? (-x) : (x)) - #define DATA_BUS_ACCESS_MODE_8BIT 0x01 #define DATA_BUS_ACCESS_MODE_16BIT 0x02 #define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10 diff --git a/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h b/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h deleted file mode 100644 index 2b3af247a1f1..000000000000 --- a/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - I2C API, implementation depends on board specifics - - Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Trident Microsystems nor Hauppauge Computer Works - nor the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - This module encapsulates I2C access.In some applications several devices - share one I2C bus. If these devices have the same I2C address some kind - off "switch" must be implemented to ensure error free communication with - one device. In case such a "switch" is used, the device ID can be used - to implement control over this "switch". -*/ - -#ifndef __BSPI2C_H__ -#define __BSPI2C_H__ - -#include "bsp_types.h" - -/* - * This structure contains the I2C address, the device ID and a user_data pointer. - * The user_data pointer can be used for application specific purposes. - */ -struct i2c_device_addr { - u16 i2c_addr; /* The I2C address of the device. */ - u16 i2c_dev_id; /* The device identifier. */ - void *user_data; /* User data pointer */ -}; - - -/* -* \def IS_I2C_10BIT( addr ) -* \brief Determine if I2C address 'addr' is a 10 bits address or not. -* \param addr The I2C address. -* \return int. -* \retval 0 if address is not a 10 bits I2C address. -* \retval 1 if address is a 10 bits I2C address. -*/ -#define IS_I2C_10BIT(addr) \ - (((addr) & 0xF8) == 0xF0) - -/*------------------------------------------------------------------------------ -Exported FUNCTIONS -------------------------------------------------------------------------------*/ - -/* -* \fn drxbsp_i2c_init() -* \brief Initialize I2C communication module. -* \return drx_status_t Return status. -* \retval 0 Initialization successful. -* \retval -EIO Initialization failed. -*/ - drx_status_t drxbsp_i2c_init(void); - -/* -* \fn drxbsp_i2c_term() -* \brief Terminate I2C communication module. -* \return drx_status_t Return status. -* \retval 0 Termination successful. -* \retval -EIO Termination failed. -*/ - drx_status_t drxbsp_i2c_term(void); - -/* -* \fn drx_status_t drxbsp_i2c_write_read( struct i2c_device_addr *w_dev_addr, -* u16 w_count, -* u8 *wData, -* struct i2c_device_addr *r_dev_addr, -* u16 r_count, -* u8 *r_data) -* \brief Read and/or write count bytes from I2C bus, store them in data[]. -* \param w_dev_addr The device i2c address and the device ID to write to -* \param w_count The number of bytes to write -* \param wData The array to write the data to -* \param r_dev_addr The device i2c address and the device ID to read from -* \param r_count The number of bytes to read -* \param r_data The array to read the data from -* \return drx_status_t Return status. -* \retval 0 Succes. -* \retval -EIO Failure. -* \retval -EINVAL Parameter 'wcount' is not zero but parameter -* 'wdata' contains NULL. -* Idem for 'rcount' and 'rdata'. -* Both w_dev_addr and r_dev_addr are NULL. -* -* This function must implement an atomic write and/or read action on the I2C bus -* No other process may use the I2C bus when this function is executing. -* The critical section of this function runs from and including the I2C -* write, up to and including the I2C read action. -* -* The device ID can be useful if several devices share an I2C address. -* It can be used to control a "switch" on the I2C bus to the correct device. -*/ - drx_status_t drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr, - u16 w_count, - u8 *w_data, - struct i2c_device_addr *r_dev_addr, - u16 r_count, u8 *r_data); - -/* -* \fn drxbsp_i2c_error_text() -* \brief Returns a human readable error. -* Counter part of numerical drx_i2c_error_g. -* -* \return char* Pointer to human readable error text. -*/ - char *drxbsp_i2c_error_text(void); - -/* -* \var drx_i2c_error_g; -* \brief I2C specific error codes, platform dependent. -*/ - extern int drx_i2c_error_g; - -#endif /* __BSPI2C_H__ */ diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 6356815cf3e1..7eb4e1469d20 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -30,6 +30,17 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); +/* + * Older drivers treated QAM64 and QAM256 the same; that is the HW always + * used "Auto" mode during detection. Setting "forced_manual"=1 allows + * the user to treat these modes as separate. For backwards compatibility, + * it's off by default. QAM_AUTO can now be specified to achive that + * effect even if "forced_manual"=1 + */ +static int forced_manual; +module_param(forced_manual, int, 0644); +MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified"); + #define DBG_INFO 1 #define DBG_REG 2 #define DBG_DUMP 4 /* FGR - comment out to remove dump code */ @@ -566,7 +577,12 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation) /* 3. : 64QAM/256QAM detection(manual, auto) */ ret = lgdt3306a_read_reg(state, 0x0009, &val); val &= 0xfc; - val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */ + /* Check for forced Manual modulation modes; otherwise always "auto" */ + if(forced_manual && (modulation != QAM_AUTO)){ + val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */ + } else { + val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */ + } ret = lgdt3306a_write_reg(state, 0x0009, val); if (lg_chkerr(ret)) goto fail; @@ -598,6 +614,28 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation) if (lg_chkerr(ret)) goto fail; + /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */ + ret = lgdt3306a_read_reg(state, 0x000a, &val); + val &= 0xfd; + val |= 0x02; + ret = lgdt3306a_write_reg(state, 0x000a, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5.2 V0.36 Control of "no signal" detector function */ + ret = lgdt3306a_read_reg(state, 0x2849, &val); + val &= 0xdf; + ret = lgdt3306a_write_reg(state, 0x2849, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */ + ret = lgdt3306a_read_reg(state, 0x302b, &val); + val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */ + ret = lgdt3306a_write_reg(state, 0x302b, val); + if (lg_chkerr(ret)) + goto fail; + /* 6. Reset */ ret = lgdt3306a_soft_reset(state); if (lg_chkerr(ret)) @@ -620,10 +658,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state, ret = lgdt3306a_set_vsb(state); break; case QAM_64: - ret = lgdt3306a_set_qam(state, QAM_64); - break; case QAM_256: - ret = lgdt3306a_set_qam(state, QAM_256); + case QAM_AUTO: + ret = lgdt3306a_set_qam(state, p->modulation); break; default: return -EINVAL; @@ -650,6 +687,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: break; default: return -EINVAL; @@ -704,6 +742,7 @@ static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: /* Auto ok for QAM */ ret = lgdt3306a_set_inversion_auto(state, 1); break; @@ -727,6 +766,7 @@ static int lgdt3306a_set_if(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: if_freq_khz = state->cfg->qam_if_khz; break; default: @@ -1585,6 +1625,7 @@ static int lgdt3306a_read_status(struct dvb_frontend *fe, switch (state->current_modulation) { case QAM_256: case QAM_64: + case QAM_AUTO: if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) { *status |= FE_HAS_VITERBI; *status |= FE_HAS_SYNC; @@ -1628,6 +1669,7 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, * Calculate some sort of "strength" from SNR */ struct lgdt3306a_state *state = fe->demodulator_priv; + u8 val; u16 snr; /* snr_x10 */ int ret; u32 ref_snr; /* snr*100 */ @@ -1640,11 +1682,15 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, ref_snr = 1600; /* 16dB */ break; case QAM_64: - ref_snr = 2200; /* 22dB */ - break; case QAM_256: - ref_snr = 2800; /* 28dB */ - break; + case QAM_AUTO: + /* need to know actual modulation to set proper SNR baseline */ + lgdt3306a_read_reg(state, 0x00a6, &val); + if(val & 0x04) + ref_snr = 2800; /* QAM-256 28dB */ + else + ref_snr = 2200; /* QAM-64 22dB */ + break; default: return -EINVAL; } @@ -2114,7 +2160,7 @@ static const struct dvb_frontend_ops lgdt3306a_ops = { .frequency_min = 54000000, .frequency_max = 858000000, .frequency_stepsize = 62500, - .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl, .init = lgdt3306a_init, @@ -2177,6 +2223,7 @@ static int lgdt3306a_probe(struct i2c_client *client, i2c_set_clientdata(client, fe->demodulator_priv); state = fe->demodulator_priv; + state->frontend.ops.release = NULL; /* create mux i2c adapter for tuner */ state->muxc = i2c_mux_alloc(client->adapter, &client->dev, @@ -2196,6 +2243,8 @@ static int lgdt3306a_probe(struct i2c_client *client, *config->i2c_adapter = state->muxc->adapter[0]; *config->fe = fe; + dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n"); + return 0; err_kfree: @@ -2203,7 +2252,7 @@ err_kfree: err_fe: kfree(config); fail: - dev_dbg(&client->dev, "failed=%d\n", ret); + dev_warn(&client->dev, "probe failed = %d\n", ret); return ret; } diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 50bce68ffd66..65d157fe76d1 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1262,11 +1262,12 @@ static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan) * New users must use I2C client binding directly! */ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, - struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter) + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c_adapter) { struct i2c_client *client; struct i2c_board_info board_info; - struct m88ds3103_platform_data pdata; + struct m88ds3103_platform_data pdata = {}; pdata.clk = cfg->clock; pdata.i2c_wr_max = cfg->i2c_wr_max; @@ -1409,6 +1410,8 @@ static int m88ds3103_probe(struct i2c_client *client, case M88DS3103_CHIP_ID: break; default: + ret = -ENODEV; + dev_err(&client->dev, "Unknown device. Chip_id=%02x\n", dev->chip_id); goto err_kfree; } diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 2969ba6ed9e1..377cd984b069 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -31,8 +31,6 @@ static unsigned int verbose = 5; module_param(verbose, int, 0644); -#define ABS(x) ((x) < 0 ? (-x) : (x)) - struct mb86a16_state { struct i2c_adapter *i2c_adap; const struct mb86a16_config *config; @@ -1202,12 +1200,12 @@ static int mb86a16_set_fe(struct mb86a16_state *state) signal_dupl = 0; for (j = 0; j < prev_freq_num; j++) { - if ((ABS(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) { + if ((abs(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) { signal_dupl = 1; dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j); } } - if ((signal_dupl == 0) && (swp_freq > 0) && (ABS(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) { + if ((signal_dupl == 0) && (swp_freq > 0) && (abs(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) { dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate); prev_swp_freq[prev_freq_num] = swp_freq; prev_freq_num++; @@ -1381,7 +1379,7 @@ static int mb86a16_set_fe(struct mb86a16_state *state) dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq); swp_freq += delta_freq; dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq); - if (ABS(state->frequency * 1000 - swp_freq) > 3800) { + if (abs(state->frequency * 1000 - swp_freq) > 3800) { dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !"); } else { diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c index e899821018a0..483ee7d6198e 100644 --- a/drivers/media/dvb-frontends/mxl5xx.c +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -380,6 +380,38 @@ static int get_algo(struct dvb_frontend *fe) return DVBFE_ALGO_HW; } +static u32 gold2root(u32 gold) +{ + u32 x, g, tmp = gold; + + if (tmp >= 0x3ffff) + tmp = 0; + for (g = 0, x = 1; g < tmp; g++) + x = (((x ^ (x >> 7)) & 1) << 17) | (x >> 1); + return x; +} + +static int cfg_scrambler(struct mxl *state, u32 gold) +{ + u32 root; + u8 buf[26] = { + MXL_HYDRA_PLID_CMD_WRITE, 24, + 0, MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD, 0, 0, + state->demod, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + }; + + root = gold2root(gold); + + buf[25] = (root >> 24) & 0xff; + buf[24] = (root >> 16) & 0xff; + buf[23] = (root >> 8) & 0xff; + buf[22] = root & 0xff; + + return send_command(state, sizeof(buf), buf); +} + static int cfg_demod_abort_tune(struct mxl *state) { struct MXL_HYDRA_DEMOD_ABORT_TUNE_T abort_tune_cmd; @@ -437,7 +469,7 @@ static int set_parameters(struct dvb_frontend *fe) demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO; demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_AUTO; demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_AUTO; - /* cfg_scrambler(state); */ + cfg_scrambler(state, p->scrambling_sequence_index); break; default: return -EINVAL; diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 94bf5b7d6f3f..fa3b8169c1a5 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -498,7 +498,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22) * / ConstWithBandwidthMode) */ - num = dev->pdata->clk * 7; + num = dev->pdata->clk * 7ULL; num *= 0x400000; num = div_u64(num, bw_mode); resamp_ratio = num & 0x3ffffff; @@ -511,7 +511,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) * / (CrystalFreqHz * 7)) */ num = bw_mode << 20; - num2 = dev->pdata->clk * 7; + num2 = dev->pdata->clk * 7ULL; num = div_u64(num, num2); num = -num; cfreq_off_ratio = num & 0xfffff; diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index aced6a956ec5..a23ba8727218 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -682,17 +682,17 @@ static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode) val = s5h1409_readreg(state, 0xac) & 0xcfff; switch (mode) { - case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: + case S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK: val |= 0x0000; break; - case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: + case S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK: dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); val |= 0x1000; break; - case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: + case S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK: val |= 0x2000; break; - case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: + case S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK: val |= 0x3000; break; default: diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h index b38557c451b9..87de58ffc822 100644 --- a/drivers/media/dvb-frontends/s5h1409.h +++ b/drivers/media/dvb-frontends/s5h1409.h @@ -52,10 +52,10 @@ struct s5h1409_config { u8 status_mode; /* MPEG signal timing */ -#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 -#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 -#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 -#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +#define S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 u16 mpeg_timing; /* HVR-1600 optimizations (to better work with MXL5005s) diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index c4b1e9725f3e..af5962807f2c 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -433,17 +433,17 @@ static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode) val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff; switch (mode) { - case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: + case S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK: val |= 0x0000; break; - case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: + case S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK: dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); val |= 0x1000; break; - case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: + case S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK: val |= 0x2000; break; - case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: + case S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK: val |= 0x3000; break; default: diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h index 791bab0e16e9..850ee713d64c 100644 --- a/drivers/media/dvb-frontends/s5h1411.h +++ b/drivers/media/dvb-frontends/s5h1411.h @@ -40,10 +40,10 @@ struct s5h1411_config { u8 gpio; /* MPEG signal timing */ -#define S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 -#define S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 -#define S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 -#define S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +#define S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 u16 mpeg_timing; /* IF Freq for QAM and VSB in KHz */ diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h index af3a157b5e77..646dda36262b 100644 --- a/drivers/media/dvb-frontends/s5h1432.h +++ b/drivers/media/dvb-frontends/s5h1432.h @@ -42,10 +42,10 @@ struct s5h1432_config { u8 gpio; /* MPEG signal timing */ -#define S5H1432_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 -#define S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 -#define S5H1432_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 -#define S5H1432_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +#define S5H1432_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1432_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1432_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 u16 mpeg_timing; /* IF Freq for QAM and VSB in KHz */ diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 539399dac551..324493e05f9f 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -82,6 +82,30 @@ err_mutex_unlock: return ret; } +static int si2168_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + struct si2168_cmd cmd; + int ret = 0; + + dev_dbg(&client->dev, "%s acquire: %d\n", __func__, acquire); + + /* set TS_MODE property */ + memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); + if (acquire) + cmd.args[4] |= dev->ts_mode; + else + cmd.args[4] |= SI2168_TS_TRISTATE; + if (dev->ts_clock_gapped) + cmd.args[4] |= 0x40; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2168_cmd_execute(client, &cmd); + + return ret; +} + static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct i2c_client *client = fe->demodulator_priv; @@ -339,6 +363,8 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); cmd.args[4] = delivery_system | bandwidth; + if (dev->spectral_inversion) + cmd.args[5] |= 1; cmd.wlen = 6; cmd.rlen = 4; ret = si2168_cmd_execute(client, &cmd); @@ -403,6 +429,11 @@ static int si2168_set_frontend(struct dvb_frontend *fe) dev->delivery_system = c->delivery_system; + /* enable ts bus */ + ret = si2168_ts_bus_ctrl(fe, 1); + if (ret) + goto err; + return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -541,13 +572,7 @@ static int si2168_init(struct dvb_frontend *fe) dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); /* set ts mode */ - memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); - cmd.args[4] |= dev->ts_mode; - if (dev->ts_clock_gapped) - cmd.args[4] |= 0x40; - cmd.wlen = 6; - cmd.rlen = 4; - ret = si2168_cmd_execute(client, &cmd); + ret = si2168_ts_bus_ctrl(fe, 1); if (ret) goto err; @@ -584,7 +609,12 @@ static int si2168_sleep(struct dvb_frontend *fe) dev->active = false; - /* Firmware B 4.0-11 or later loses warm state during sleep */ + /* tri-state data bus */ + ret = si2168_ts_bus_ctrl(fe, 0); + if (ret) + goto err; + + /* Firmware later than B 4.0-11 loses warm state during sleep */ if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) dev->warm = false; @@ -776,6 +806,7 @@ 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->spectral_inversion = config->spectral_inversion; dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n", dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, @@ -788,7 +819,7 @@ static int si2168_probe(struct i2c_client *client, err_kfree: kfree(dev); err: - dev_dbg(&client->dev, "failed=%d\n", ret); + dev_warn(&client->dev, "probe failed = %d\n", ret); return ret; } diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h index 3225d0cc93c7..d519edd26c21 100644 --- a/drivers/media/dvb-frontends/si2168.h +++ b/drivers/media/dvb-frontends/si2168.h @@ -38,6 +38,7 @@ struct si2168_config { /* TS mode */ #define SI2168_TS_PARALLEL 0x06 #define SI2168_TS_SERIAL 0x03 +#define SI2168_TS_TRISTATE 0x00 u8 ts_mode; /* TS clock inverted */ @@ -45,6 +46,9 @@ struct si2168_config { /* TS clock gapped */ bool ts_clock_gapped; + + /* Inverted spectrum */ + bool spectral_inversion; }; #endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 3c8746a20038..2d362e162ade 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -48,6 +48,7 @@ struct si2168_dev { u8 ts_mode; bool ts_clock_inv; bool ts_clock_gapped; + bool spectral_inversion; }; /* firmware command struct */ diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c index 572a297811fe..f39d566d7d1d 100644 --- a/drivers/media/dvb-frontends/sp887x.c +++ b/drivers/media/dvb-frontends/sp887x.c @@ -136,7 +136,7 @@ static void sp887x_setup_agc (struct sp887x_state* state) static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) { struct sp887x_state* state = fe->demodulator_priv; - u8 buf [BLOCKSIZE+2]; + u8 buf [BLOCKSIZE + 2]; int i; int fw_size = fw->size; const unsigned char *mem = fw->data; @@ -144,7 +144,7 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware dprintk("%s\n", __func__); /* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */ - if (fw_size < FW_SIZE+10) + if (fw_size < FW_SIZE + 10) return -ENODEV; mem = fw->data + 10; @@ -167,7 +167,7 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware int c = BLOCKSIZE; int err; - if (i+c > FW_SIZE) + if (c > FW_SIZE - i) c = FW_SIZE - i; /* bit 0x8000 in address is set to enable 13bit mode */ diff --git a/drivers/media/dvb-frontends/stb0899_reg.h b/drivers/media/dvb-frontends/stb0899_reg.h index ba1ed56304a0..f564269249a6 100644 --- a/drivers/media/dvb-frontends/stb0899_reg.h +++ b/drivers/media/dvb-frontends/stb0899_reg.h @@ -374,22 +374,22 @@ #define STB0899_OFF0_IF_AGC_GAIN 0xf30c #define STB0899_BASE_IF_AGC_GAIN 0x00000000 -#define STB0899_IF_AGC_GAIN (0x3fff < 0) +#define STB0899_IF_AGC_GAIN (0x3fff << 0) #define STB0899_OFFST_IF_AGC_GAIN 0 #define STB0899_WIDTH_IF_AGC_GAIN 14 #define STB0899_OFF0_BB_AGC_GAIN 0xf310 #define STB0899_BASE_BB_AGC_GAIN 0x00000000 -#define STB0899_BB_AGC_GAIN (0x3fff < 0) +#define STB0899_BB_AGC_GAIN (0x3fff << 0) #define STB0899_OFFST_BB_AGC_GAIN 0 #define STB0899_WIDTH_BB_AGC_GAIN 14 #define STB0899_OFF0_DC_OFFSET 0xf314 #define STB0899_BASE_DC_OFFSET 0x00000000 -#define STB0899_I (0xff < 8) +#define STB0899_I (0xff << 8) #define STB0899_OFFST_I 8 #define STB0899_WIDTH_I 8 -#define STB0899_Q (0xff < 0) +#define STB0899_Q (0xff << 0) #define STB0899_OFFST_Q 8 #define STB0899_WIDTH_Q 8 diff --git a/drivers/media/dvb-frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h index 8abc451dd524..460066a391b7 100644 --- a/drivers/media/dvb-frontends/stv0367_priv.h +++ b/drivers/media/dvb-frontends/stv0367_priv.h @@ -35,7 +35,6 @@ #endif /* MACRO definitions */ -#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) #define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) #define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) #define INRANGE(X, Y, Z) \ diff --git a/drivers/media/dvb-frontends/stv0900_priv.h b/drivers/media/dvb-frontends/stv0900_priv.h index d1fc06ff27d3..09a46477eae4 100644 --- a/drivers/media/dvb-frontends/stv0900_priv.h +++ b/drivers/media/dvb-frontends/stv0900_priv.h @@ -24,7 +24,6 @@ #include <linux/i2c.h> -#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) #define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \ || (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c index c97a39120ea5..d406c83e4744 100644 --- a/drivers/media/dvb-frontends/stv0900_sw.c +++ b/drivers/media/dvb-frontends/stv0900_sw.c @@ -1255,14 +1255,14 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) else intp->freq[d] = stv0900_get_tuner_freq(fe); - if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) range = STV0900_RANGEOK; - else if (ABS(offsetFreq) <= + else if (abs(offsetFreq) <= (stv0900_carrier_width(result->symbol_rate, result->rolloff) / 2000)) range = STV0900_RANGEOK; - } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + } else if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) range = STV0900_RANGEOK; dprintk("%s: range %d\n", __func__, range); diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index a2f7c0c1587f..52355c14fd64 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -1673,15 +1673,15 @@ static int send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) { struct stv *state = fe->demodulator_priv; - u16 offs = state->nr ? 0x40 : 0; int i; - write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3E); + SET_FIELD(DISEQC_MODE, 2); + SET_FIELD(DIS_PRECHARGE, 1); for (i = 0; i < cmd->msg_len; i++) { wait_dis(state, 0x40, 0x00); - write_reg(state, RSTV0910_P1_DISTXFIFO + offs, cmd->msg[i]); + SET_REG(DISTXFIFO, cmd->msg[i]); } - write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3A); + SET_FIELD(DIS_PRECHARGE, 0); wait_dis(state, 0x20, 0x20); return 0; } @@ -1689,19 +1689,20 @@ static int send_master_cmd(struct dvb_frontend *fe, static int send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst) { struct stv *state = fe->demodulator_priv; - u16 offs = state->nr ? 0x40 : 0; u8 value; if (burst == SEC_MINI_A) { - write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3F); + SET_FIELD(DISEQC_MODE, 3); value = 0x00; } else { - write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3E); + SET_FIELD(DISEQC_MODE, 2); value = 0xFF; } + + SET_FIELD(DIS_PRECHARGE, 1); wait_dis(state, 0x40, 0x00); - write_reg(state, RSTV0910_P1_DISTXFIFO + offs, value); - write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3A); + SET_REG(DISTXFIFO, value); + SET_FIELD(DIS_PRECHARGE, 0); wait_dis(state, 0x20, 0x20); return 0; diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c index 1d8979289915..17600989f121 100644 --- a/drivers/media/dvb-frontends/ves1820.c +++ b/drivers/media/dvb-frontends/ves1820.c @@ -137,7 +137,7 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) NDEC = 3; /* yeuch! */ - fpxin = state->config->xin * 10; + fpxin = state->config->xin * 10ULL; fptmp = fpxin; do_div(fptmp, 123); if (symbolrate < fptmp) SFIL = 1; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9f18cd296841..541f0d28afd8 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -56,6 +56,17 @@ config VIDEO_TDA9840 To compile this driver as a module, choose M here: the module will be called tda9840. +config VIDEO_TDA1997X + tristate "NXP TDA1997x HDMI receiver" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on SND_SOC + select SND_PCM + ---help--- + V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. + + To compile this driver as a module, choose M here: the + module will be called tda1997x. + config VIDEO_TEA6415C tristate "Philips TEA6415C audio processor" depends on I2C @@ -423,6 +434,15 @@ config VIDEO_TW9906 To compile this driver as a module, choose M here: the module will be called tw9906. +config VIDEO_TW9910 + tristate "Techwell TW9910 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for Techwell TW9910 NTSC/PAL/SECAM video decoder. + + To compile this driver as a module, choose M here: the + module will be called tw9910. + config VIDEO_VPX3220 tristate "vpx3220a, vpx3216b & vpx3214c video decoders" depends on VIDEO_V4L2 && I2C @@ -586,6 +606,18 @@ config VIDEO_OV2659 To compile this driver as a module, choose M here: the module will be called ov2659. +config VIDEO_OV2685 + tristate "OmniVision OV2685 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV2685 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2685. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF @@ -645,6 +677,28 @@ config VIDEO_OV5670 To compile this driver as a module, choose M here: the module will be called ov5670. +config VIDEO_OV5695 + tristate "OmniVision OV5695 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV5695 camera. + + To compile this driver as a module, choose M here: the + module will be called ov5695. + +config VIDEO_OV772X + tristate "OmniVision OV772x sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV772x camera. + + To compile this driver as a module, choose M here: the + module will be called ov772x. + config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" depends on I2C && VIDEO_V4L2 @@ -660,6 +714,7 @@ config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE ---help--- This is a Video4Linux2 sensor-level driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 @@ -733,6 +788,17 @@ config VIDEO_MT9T001 This is a Video4Linux2 sensor-level driver for the Aptina (Micron) mt0t001 3 Mpixel camera. +config VIDEO_MT9T112 + tristate "Aptina MT9T111/MT9T112 support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) MT9T111 and MT9T112 3 Mpixel camera. + + To compile this driver as a module, choose M here: the + module will be called mt9t112. + config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c0f94cd8d56d..ea34aee1a85a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o @@ -48,6 +49,7 @@ obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o obj-$(CONFIG_VIDEO_TW2804) += tw2804.o obj-$(CONFIG_VIDEO_TW9903) += tw9903.o obj-$(CONFIG_VIDEO_TW9906) += tw9906.o +obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_CS3308) += cs3308.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o @@ -61,13 +63,16 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV2685) += ov2685.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o +obj-$(CONFIG_VIDEO_OV5695) += ov5695.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o @@ -75,6 +80,7 @@ obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o +obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index a056d6cdaaaa..91ff06088572 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Analog Devices AD9389B/AD9889B video encoder driver * * Copyright 2012 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. */ /* diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index fd92c9e4b519..6ca88daa0ecd 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -35,96 +35,28 @@ * Register manipulation */ -static const struct regmap_config adv748x_regmap_cnf[] = { - { - .name = "io", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "dpll", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "cp", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "hdmi", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "edid", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "repeater", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "infoframe", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "cec", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "sdp", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - - { - .name = "txb", - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, - { - .name = "txa", - .reg_bits = 8, - .val_bits = 8, +#define ADV748X_REGMAP_CONF(n) \ +{ \ + .name = n, \ + .reg_bits = 8, \ + .val_bits = 8, \ + .max_register = 0xff, \ + .cache_type = REGCACHE_NONE, \ +} - .max_register = 0xff, - .cache_type = REGCACHE_NONE, - }, +static const struct regmap_config adv748x_regmap_cnf[] = { + ADV748X_REGMAP_CONF("io"), + ADV748X_REGMAP_CONF("dpll"), + ADV748X_REGMAP_CONF("cp"), + ADV748X_REGMAP_CONF("hdmi"), + ADV748X_REGMAP_CONF("edid"), + ADV748X_REGMAP_CONF("repeater"), + ADV748X_REGMAP_CONF("infoframe"), + ADV748X_REGMAP_CONF("cbus"), + ADV748X_REGMAP_CONF("cec"), + ADV748X_REGMAP_CONF("sdp"), + ADV748X_REGMAP_CONF("txa"), + ADV748X_REGMAP_CONF("txb"), }; static int adv748x_configure_regmap(struct adv748x_state *state, int region) @@ -148,20 +80,24 @@ static int adv748x_configure_regmap(struct adv748x_state *state, int region) return 0; } +struct adv748x_register_map { + const char *name; + u8 default_addr; +}; -/* Default addresses for the I2C pages */ -static int adv748x_i2c_addresses[ADV748X_PAGE_MAX] = { - ADV748X_I2C_IO, - ADV748X_I2C_DPLL, - ADV748X_I2C_CP, - ADV748X_I2C_HDMI, - ADV748X_I2C_EDID, - ADV748X_I2C_REPEATER, - ADV748X_I2C_INFOFRAME, - ADV748X_I2C_CEC, - ADV748X_I2C_SDP, - ADV748X_I2C_TXB, - ADV748X_I2C_TXA, +static const struct adv748x_register_map adv748x_default_addresses[] = { + [ADV748X_PAGE_IO] = { "main", 0x70 }, + [ADV748X_PAGE_DPLL] = { "dpll", 0x26 }, + [ADV748X_PAGE_CP] = { "cp", 0x22 }, + [ADV748X_PAGE_HDMI] = { "hdmi", 0x34 }, + [ADV748X_PAGE_EDID] = { "edid", 0x36 }, + [ADV748X_PAGE_REPEATER] = { "repeater", 0x32 }, + [ADV748X_PAGE_INFOFRAME] = { "infoframe", 0x31 }, + [ADV748X_PAGE_CBUS] = { "cbus", 0x30 }, + [ADV748X_PAGE_CEC] = { "cec", 0x41 }, + [ADV748X_PAGE_SDP] = { "sdp", 0x79 }, + [ADV748X_PAGE_TXB] = { "txb", 0x48 }, + [ADV748X_PAGE_TXA] = { "txa", 0x4a }, }; static int adv748x_read_check(struct adv748x_state *state, @@ -210,15 +146,20 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, return regmap_raw_write(regmap, init_reg, val, val_len); } -static struct i2c_client *adv748x_dummy_client(struct adv748x_state *state, - u8 addr, u8 io_reg) +static int adv748x_set_slave_addresses(struct adv748x_state *state) { - struct i2c_client *client = state->client; + struct i2c_client *client; + unsigned int i; + u8 io_reg; - if (addr) - io_write(state, io_reg, addr << 1); + for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) { + io_reg = ADV748X_IO_SLAVE_ADDR_BASE + i; + client = state->i2c_clients[i]; + + io_write(state, io_reg, client->addr << 1); + } - return i2c_new_dummy(client->adapter, io_read(state, io_reg) >> 1); + return 0; } static void adv748x_unregister_clients(struct adv748x_state *state) @@ -231,13 +172,15 @@ static void adv748x_unregister_clients(struct adv748x_state *state) static int adv748x_initialise_clients(struct adv748x_state *state) { - int i; + unsigned int i; int ret; for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) { - state->i2c_clients[i] = - adv748x_dummy_client(state, adv748x_i2c_addresses[i], - ADV748X_IO_SLAVE_ADDR_BASE + i); + state->i2c_clients[i] = i2c_new_secondary_device( + state->client, + adv748x_default_addresses[i].name, + adv748x_default_addresses[i].default_addr); + if (state->i2c_clients[i] == NULL) { adv_err(state, "failed to create i2c client %u\n", i); return -ENOMEM; @@ -248,7 +191,7 @@ static int adv748x_initialise_clients(struct adv748x_state *state) return ret; } - return 0; + return adv748x_set_slave_addresses(state); } /** @@ -414,20 +357,6 @@ static const struct adv748x_reg_value adv748x_sw_reset[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -static const struct adv748x_reg_value adv748x_set_slave_address[] = { - {ADV748X_PAGE_IO, 0xf3, ADV748X_I2C_DPLL << 1}, - {ADV748X_PAGE_IO, 0xf4, ADV748X_I2C_CP << 1}, - {ADV748X_PAGE_IO, 0xf5, ADV748X_I2C_HDMI << 1}, - {ADV748X_PAGE_IO, 0xf6, ADV748X_I2C_EDID << 1}, - {ADV748X_PAGE_IO, 0xf7, ADV748X_I2C_REPEATER << 1}, - {ADV748X_PAGE_IO, 0xf8, ADV748X_I2C_INFOFRAME << 1}, - {ADV748X_PAGE_IO, 0xfa, ADV748X_I2C_CEC << 1}, - {ADV748X_PAGE_IO, 0xfb, ADV748X_I2C_SDP << 1}, - {ADV748X_PAGE_IO, 0xfc, ADV748X_I2C_TXB << 1}, - {ADV748X_PAGE_IO, 0xfd, ADV748X_I2C_TXA << 1}, - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; - /* Supported Formats For Script Below */ /* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { @@ -558,7 +487,7 @@ static int adv748x_reset(struct adv748x_state *state) if (ret < 0) return ret; - ret = adv748x_write_regs(state, adv748x_set_slave_address); + ret = adv748x_set_slave_addresses(state); if (ret < 0) return ret; @@ -715,7 +644,7 @@ static int adv748x_probe(struct i2c_client *client, ret = adv748x_identify_chip(state); if (ret) { adv_err(state, "Failed to identify chip"); - goto err_cleanup_clients; + goto err_cleanup_dt; } /* Configure remaining pages as I2C clients with regmap access */ diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 4da4253553fc..10d229a4f088 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -105,6 +105,9 @@ static void adv748x_hdmi_fill_format(struct adv748x_hdmi *hdmi, fmt->width = hdmi->timings.bt.width; fmt->height = hdmi->timings.bt.height; + + if (fmt->field == V4L2_FIELD_ALTERNATE) + fmt->height /= 2; } static void adv748x_fill_optional_dv_timings(struct v4l2_dv_timings *timings) diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 6789e2f3bc8c..65f83741277e 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -27,19 +27,6 @@ #ifndef _ADV748X_H_ #define _ADV748X_H_ -/* I2C slave addresses */ -#define ADV748X_I2C_IO 0x70 /* IO Map */ -#define ADV748X_I2C_DPLL 0x26 /* DPLL Map */ -#define ADV748X_I2C_CP 0x22 /* CP Map */ -#define ADV748X_I2C_HDMI 0x34 /* HDMI Map */ -#define ADV748X_I2C_EDID 0x36 /* EDID Map */ -#define ADV748X_I2C_REPEATER 0x32 /* HDMI RX Repeater Map */ -#define ADV748X_I2C_INFOFRAME 0x31 /* HDMI RX InfoFrame Map */ -#define ADV748X_I2C_CEC 0x41 /* CEC Map */ -#define ADV748X_I2C_SDP 0x79 /* SDP Map */ -#define ADV748X_I2C_TXB 0x48 /* CSI-TXB Map */ -#define ADV748X_I2C_TXA 0x4a /* CSI-TXA Map */ - enum adv748x_page { ADV748X_PAGE_IO, ADV748X_PAGE_DPLL, @@ -48,6 +35,7 @@ enum adv748x_page { ADV748X_PAGE_EDID, ADV748X_PAGE_REPEATER, ADV748X_PAGE_INFOFRAME, + ADV748X_PAGE_CBUS, ADV748X_PAGE_CEC, ADV748X_PAGE_SDP, ADV748X_PAGE_TXB, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 2817bafc67bf..d23505a411ee 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Analog Devices ADV7511 HDMI Transmitter Device Driver * * Copyright 2013 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. */ diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1544920ec52d..cac2081e876e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * adv7604 - Analog Devices ADV7604 video decoder driver * * Copyright 2012 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. - * */ /* @@ -2734,6 +2722,27 @@ static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = { /* ----------------------------------------------------------------------- */ +struct adv76xx_register_map { + const char *name; + u8 default_addr; +}; + +static const struct adv76xx_register_map adv76xx_default_addresses[] = { + [ADV76XX_PAGE_IO] = { "main", 0x4c }, + [ADV7604_PAGE_AVLINK] = { "avlink", 0x42 }, + [ADV76XX_PAGE_CEC] = { "cec", 0x40 }, + [ADV76XX_PAGE_INFOFRAME] = { "infoframe", 0x3e }, + [ADV7604_PAGE_ESDP] = { "esdp", 0x38 }, + [ADV7604_PAGE_DPP] = { "dpp", 0x3c }, + [ADV76XX_PAGE_AFE] = { "afe", 0x26 }, + [ADV76XX_PAGE_REP] = { "rep", 0x32 }, + [ADV76XX_PAGE_EDID] = { "edid", 0x36 }, + [ADV76XX_PAGE_HDMI] = { "hdmi", 0x34 }, + [ADV76XX_PAGE_TEST] = { "test", 0x30 }, + [ADV76XX_PAGE_CP] = { "cp", 0x22 }, + [ADV7604_PAGE_VDP] = { "vdp", 0x24 }, +}; + static int adv76xx_core_init(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -2834,13 +2843,26 @@ static void adv76xx_unregister_clients(struct adv76xx_state *state) } static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, - u8 addr, u8 io_reg) + unsigned int page) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv76xx_state *state = to_state(sd); + struct adv76xx_platform_data *pdata = &state->pdata; + unsigned int io_reg = 0xf2 + page; + struct i2c_client *new_client; + + if (pdata && pdata->i2c_addresses[page]) + new_client = i2c_new_dummy(client->adapter, + pdata->i2c_addresses[page]); + else + new_client = i2c_new_secondary_device(client, + adv76xx_default_addresses[page].name, + adv76xx_default_addresses[page].default_addr); + + if (new_client) + io_write(sd, io_reg, new_client->addr << 1); - if (addr) - io_write(sd, io_reg, addr << 1); - return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + return new_client; } static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = { @@ -3115,20 +3137,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) /* Disable the interrupt for now as no DT-based board uses it. */ state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED; - /* Use the default I2C addresses. */ - state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42; - state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40; - state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e; - state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38; - state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c; - state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26; - state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32; - state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36; - state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34; - state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30; - state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22; - state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24; - /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; state->pdata.disable_cable_det_rst = 0; @@ -3478,11 +3486,9 @@ static int adv76xx_probe(struct i2c_client *client, if (!(BIT(i) & state->info->page_mask)) continue; - state->i2c_clients[i] = - adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i], - 0xf2 + i); + state->i2c_clients[i] = adv76xx_dummy_client(sd, i); if (!state->i2c_clients[i]) { - err = -ENOMEM; + err = -EINVAL; v4l2_err(sd, "failed to create i2c client %u\n", i); goto err_i2c; } diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 136aa80a834b..fddac32e5051 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * adv7842 - Analog Devices ADV7842 video decoder driver * * Copyright 2013 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. - * */ /* diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 98be63ae8590..b168bf3635b6 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -463,8 +463,13 @@ static void cx23885_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u32 clk_freq = 0; struct workqueue_struct *q; + /* cx23885 sets hostdata to clk_freq pointer */ + if (v4l2_get_subdev_hostdata(&state->sd)) + clk_freq = *((u32 *)v4l2_get_subdev_hostdata(&state->sd)); + /* * Come out of digital power down * The CX23888, at least, needs this, otherwise registers aside from @@ -500,8 +505,13 @@ static void cx23885_initialize(struct i2c_client *client) * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz * 572.73 MHz before post divide */ - /* HVR1850 or 50MHz xtal */ - cx25840_write(client, 0x2, 0x71); + if (clk_freq == 25000000) { + /* 888/ImpactVCBe or 25Mhz xtal */ + ; /* nothing to do */ + } else { + /* HVR1850 or 50MHz xtal */ + cx25840_write(client, 0x2, 0x71); + } cx25840_write4(client, 0x11c, 0x01d1744c); cx25840_write4(client, 0x118, 0x00000416); cx25840_write4(client, 0x404, 0x0010253e); @@ -544,9 +554,15 @@ static void cx23885_initialize(struct i2c_client *client) /* HVR1850 */ switch (state->id) { case CX23888_AV: - /* 888/HVR1250 specific */ - cx25840_write4(client, 0x10c, 0x13333333); - cx25840_write4(client, 0x108, 0x00000515); + if (clk_freq == 25000000) { + /* 888/ImpactVCBe or 25MHz xtal */ + cx25840_write4(client, 0x10c, 0x01b6db7b); + cx25840_write4(client, 0x108, 0x00000512); + } else { + /* 888/HVR1250 or 50MHz xtal */ + cx25840_write4(client, 0x10c, 0x13333333); + cx25840_write4(client, 0x108, 0x00000515); + } break; default: cx25840_write4(client, 0x10c, 0x002be2c9); @@ -576,7 +592,7 @@ static void cx23885_initialize(struct i2c_client *client) * 368.64 MHz before post divide * 122.88 MHz / 0xa = 12.288 MHz */ - /* HVR1850 or 50MHz xtal */ + /* HVR1850 or 50MHz xtal or 25MHz xtal */ cx25840_write4(client, 0x114, 0x017dbf48); cx25840_write4(client, 0x110, 0x000a030e); break; diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 193020d64e51..a7e23bcf845c 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -168,11 +168,15 @@ static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { dev_dbg(&ir->rc->dev, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -185,11 +189,15 @@ static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char buf[4]; /* poll IR chip */ - if (4 != i2c_master_recv(ir->c, buf, 4)) { + rc = i2c_master_recv(ir->c, buf, 4); + if (rc != 4) { dev_dbg(&ir->rc->dev, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -209,11 +217,15 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { dev_dbg(&ir->rc->dev, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -571,7 +583,7 @@ static int zilog_ir_format(struct rc_dev *rcdev, unsigned int *txbuf, /* first copy any leading non-repeating */ int leading = c - rep * 3; - if (leading + rep >= ARRAY_SIZE(code_block->codes) - 3) { + if (leading >= ARRAY_SIZE(code_block->codes) - 3 - rep) { dev_warn(&rcdev->dev, "IR too long, cannot transmit\n"); return -EINVAL; } diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 2f1966bdc473..87cba15b2977 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -643,7 +643,7 @@ static int max2175_set_nco_freq(struct max2175 *ctx, s32 nco_freq) if (abs_nco_freq < clock_rate / 2) { nco_val_desired = 2 * nco_freq; } else { - nco_val_desired = 2 * (clock_rate - abs_nco_freq); + nco_val_desired = 2LL * (clock_rate - abs_nco_freq); if (nco_freq < 0) nco_val_desired = -nco_val_desired; } diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c index 4dd01e9f553b..dc6cb8d475b3 100644 --- a/drivers/media/i2c/msp3400-kthreads.c +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -885,7 +885,7 @@ static int msp34xxg_modus(struct i2c_client *client) } static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) - { +{ struct msp_state *state = to_state(i2c_get_clientdata(client)); int source, matrix; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c new file mode 100644 index 000000000000..af8cca984215 --- /dev/null +++ b/drivers/media/i2c/mt9t112.c @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt9t112 Camera Driver + * + * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org> + * + * Copyright (C) 2009 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov772x driver, mt9m111 driver, + * + * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com> + * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * TODO: This driver lacks support for frame rate control due to missing + * register level documentation and suitable hardware for testing. + * v4l-utils compliance tools will report errors. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> + +#include <media/i2c/mt9t112.h> +#include <media/v4l2-common.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-subdev.h> + +/* you can check PLL/clock info */ +/* #define EXT_CLOCK 24000000 */ + +/************************************************************************ + * macro + ***********************************************************************/ +/* + * frame size + */ +#define MAX_WIDTH 2048 +#define MAX_HEIGHT 1536 + +/* + * macro of read/write + */ +#define ECHECKER(ret, x) \ + do { \ + (ret) = (x); \ + if ((ret) < 0) \ + return (ret); \ + } while (0) + +#define mt9t112_reg_write(ret, client, a, b) \ + ECHECKER(ret, __mt9t112_reg_write(client, a, b)) +#define mt9t112_mcu_write(ret, client, a, b) \ + ECHECKER(ret, __mt9t112_mcu_write(client, a, b)) + +#define mt9t112_reg_mask_set(ret, client, a, b, c) \ + ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c)) +#define mt9t112_mcu_mask_set(ret, client, a, b, c) \ + ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c)) + +#define mt9t112_reg_read(ret, client, a) \ + ECHECKER(ret, __mt9t112_reg_read(client, a)) + +/* + * Logical address + */ +#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff)) +#define VAR(id, offset) _VAR(id, offset, 0x0000) +#define VAR8(id, offset) _VAR(id, offset, 0x8000) + +/************************************************************************ + * struct + ***********************************************************************/ +struct mt9t112_format { + u32 code; + enum v4l2_colorspace colorspace; + u16 fmt; + u16 order; +}; + +struct mt9t112_priv { + struct v4l2_subdev subdev; + struct mt9t112_platform_data *info; + struct i2c_client *client; + struct v4l2_rect frame; + struct clk *clk; + struct gpio_desc *standby_gpio; + const struct mt9t112_format *format; + int num_formats; + bool init_done; +}; + +/************************************************************************ + * supported format + ***********************************************************************/ + +static const struct mt9t112_format mt9t112_cfmts[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 1, + .order = 0, + }, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 1, + .order = 1, + }, { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 1, + .order = 2, + }, { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 1, + .order = 3, + }, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 8, + .order = 2, + }, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .fmt = 4, + .order = 2, + }, +}; + +/************************************************************************ + * general function + ***********************************************************************/ +static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), + struct mt9t112_priv, + subdev); +} + +static int __mt9t112_reg_read(const struct i2c_client *client, u16 command) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + int ret; + + command = swab16(command); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = (u8 *)&command; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = buf; + + /* + * If return value of this function is < 0, it means error, else, + * below 16bit is valid data. + */ + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + + memcpy(&ret, buf, 2); + + return swab16(ret); +} + +static int __mt9t112_reg_write(const struct i2c_client *client, + u16 command, u16 data) +{ + struct i2c_msg msg; + u8 buf[4]; + int ret; + + command = swab16(command); + data = swab16(data); + + memcpy(buf + 0, &command, 2); + memcpy(buf + 2, &data, 2); + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 4; + msg.buf = buf; + + /* + * i2c_transfer return message length, but this function should + * return 0 if correct case. + */ + ret = i2c_transfer(client->adapter, &msg, 1); + + return ret >= 0 ? 0 : ret; +} + +static int __mt9t112_reg_mask_set(const struct i2c_client *client, + u16 command, u16 mask, u16 set) +{ + int val = __mt9t112_reg_read(client, command); + + if (val < 0) + return val; + + val &= ~mask; + val |= set & mask; + + return __mt9t112_reg_write(client, command, val); +} + +/* mcu access */ +static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command) +{ + int ret; + + ret = __mt9t112_reg_write(client, 0x098E, command); + if (ret < 0) + return ret; + + return __mt9t112_reg_read(client, 0x0990); +} + +static int __mt9t112_mcu_write(const struct i2c_client *client, + u16 command, u16 data) +{ + int ret; + + ret = __mt9t112_reg_write(client, 0x098E, command); + if (ret < 0) + return ret; + + return __mt9t112_reg_write(client, 0x0990, data); +} + +static int __mt9t112_mcu_mask_set(const struct i2c_client *client, + u16 command, u16 mask, u16 set) +{ + int val = __mt9t112_mcu_read(client, command); + + if (val < 0) + return val; + + val &= ~mask; + val |= set & mask; + + return __mt9t112_mcu_write(client, command, val); +} + +static int mt9t112_reset(const struct i2c_client *client) +{ + int ret; + + mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001); + usleep_range(1000, 5000); + mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000); + + return ret; +} + +#ifndef EXT_CLOCK +#define CLOCK_INFO(a, b) +#else +#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b) +static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) +{ + int m, n, p1, p2, p3, p4, p5, p6, p7; + u32 vco, clk; + char *enable; + + ext /= 1000; /* kbyte order */ + + mt9t112_reg_read(n, client, 0x0012); + p1 = n & 0x000f; + n = n >> 4; + p2 = n & 0x000f; + n = n >> 4; + p3 = n & 0x000f; + + mt9t112_reg_read(n, client, 0x002a); + p4 = n & 0x000f; + n = n >> 4; + p5 = n & 0x000f; + n = n >> 4; + p6 = n & 0x000f; + + mt9t112_reg_read(n, client, 0x002c); + p7 = n & 0x000f; + + mt9t112_reg_read(n, client, 0x0010); + m = n & 0x00ff; + n = (n >> 8) & 0x003f; + + enable = ((ext < 6000) || (ext > 54000)) ? "X" : ""; + dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); + + vco = 2 * m * ext / (n + 1); + enable = ((vco < 384000) || (vco > 768000)) ? "X" : ""; + dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); + + clk = vco / (p1 + 1) / (p2 + 1); + enable = (clk > 96000) ? "X" : ""; + dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); + + clk = vco / (p3 + 1); + enable = (clk > 768000) ? "X" : ""; + dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); + + clk = vco / (p6 + 1); + enable = (clk > 96000) ? "X" : ""; + dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); + + clk = vco / (p5 + 1); + enable = (clk > 54000) ? "X" : ""; + dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); + + clk = vco / (p4 + 1); + enable = (clk > 70000) ? "X" : ""; + dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); + + clk = vco / (p7 + 1); + dev_dbg(&client->dev, "External sensor : %10u K\n", clk); + + clk = ext / (n + 1); + enable = ((clk < 2000) || (clk > 24000)) ? "X" : ""; + dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); + + return 0; +} +#endif + +static int mt9t112_set_a_frame_size(const struct i2c_client *client, + u16 width, u16 height) +{ + int ret; + u16 wstart = (MAX_WIDTH - width) / 2; + u16 hstart = (MAX_HEIGHT - height) / 2; + + /* (Context A) Image Width/Height. */ + mt9t112_mcu_write(ret, client, VAR(26, 0), width); + mt9t112_mcu_write(ret, client, VAR(26, 2), height); + + /* (Context A) Output Width/Height. */ + mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width); + mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height); + + /* (Context A) Start Row/Column. */ + mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart); + mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart); + + /* (Context A) End Row/Column. */ + mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart); + mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart); + + mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); + + return ret; +} + +static int mt9t112_set_pll_dividers(const struct i2c_client *client, + u8 m, u8 n, u8 p1, u8 p2, u8 p3, u8 p4, + u8 p5, u8 p6, u8 p7) +{ + int ret; + u16 val; + + /* N/M */ + val = (n << 8) | (m << 0); + mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val); + + /* P1/P2/P3 */ + val = ((p3 & 0x0F) << 8) | ((p2 & 0x0F) << 4) | ((p1 & 0x0F) << 0); + mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val); + + /* P4/P5/P6 */ + val = (0x7 << 12) | ((p6 & 0x0F) << 8) | ((p5 & 0x0F) << 4) | + ((p4 & 0x0F) << 0); + mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val); + + /* P7 */ + val = (0x1 << 12) | ((p7 & 0x0F) << 0); + mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val); + + return ret; +} + +static int mt9t112_init_pll(const struct i2c_client *client) +{ + struct mt9t112_priv *priv = to_mt9t112(client); + int data, i, ret; + + mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001); + + /* PLL control: BYPASS PLL = 8517. */ + mt9t112_reg_write(ret, client, 0x0014, 0x2145); + + /* Replace these registers when new timing parameters are generated. */ + mt9t112_set_pll_dividers(client, + priv->info->divider.m, priv->info->divider.n, + priv->info->divider.p1, priv->info->divider.p2, + priv->info->divider.p3, priv->info->divider.p4, + priv->info->divider.p5, priv->info->divider.p6, + priv->info->divider.p7); + + /* + * TEST_BYPASS on + * PLL_ENABLE on + * SEL_LOCK_DET on + * TEST_BYPASS off + */ + mt9t112_reg_write(ret, client, 0x0014, 0x2525); + mt9t112_reg_write(ret, client, 0x0014, 0x2527); + mt9t112_reg_write(ret, client, 0x0014, 0x3427); + mt9t112_reg_write(ret, client, 0x0014, 0x3027); + + mdelay(10); + + /* + * PLL_BYPASS off + * Reference clock count + * I2C Master Clock Divider + */ + mt9t112_reg_write(ret, client, 0x0014, 0x3046); + /* JPEG initialization workaround */ + mt9t112_reg_write(ret, client, 0x0016, 0x0400); + mt9t112_reg_write(ret, client, 0x0022, 0x0190); + mt9t112_reg_write(ret, client, 0x3B84, 0x0212); + + /* External sensor clock is PLL bypass. */ + mt9t112_reg_write(ret, client, 0x002E, 0x0500); + + mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002); + mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004); + + /* MCU disabled. */ + mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004); + + /* Out of standby. */ + mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0); + + mdelay(50); + + /* + * Standby Workaround + * Disable Secondary I2C Pads + */ + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + mt9t112_reg_write(ret, client, 0x0614, 0x0001); + mdelay(1); + + /* Poll to verify out of standby. Must Poll this bit. */ + for (i = 0; i < 100; i++) { + mt9t112_reg_read(data, client, 0x0018); + if (!(data & 0x4000)) + break; + + mdelay(10); + } + + return ret; +} + +static int mt9t112_init_setting(const struct i2c_client *client) +{ + int ret; + + /* Adaptive Output Clock (A) */ + mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000); + + /* Read Mode (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024); + + /* Fine Correction (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC); + + /* Fine IT Min (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1); + + /* Fine IT Max Margin (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF); + + /* Base Frame Lines (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D); + + /* Min Line Length (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a); + + /* Line Length (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0); + + /* Adaptive Output Clock (B) */ + mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000); + + /* Row Start (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004); + + /* Column Start (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004); + + /* Row End (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B); + + /* Column End (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B); + + /* Fine Correction (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C); + + /* Fine IT Min (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1); + + /* Fine IT Max Margin (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF); + + /* Base Frame Lines (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668); + + /* Min Line Length (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0); + + /* Line Length (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0); + + /* + * Flicker Dectection registers. + * This section should be replaced whenever new timing file is + * generated. All the following registers need to be replaced. + * Following registers are generated from Register Wizard but user can + * modify them. For detail see auto flicker detection tuning. + */ + + /* FD_FDPERIOD_SELECT */ + mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01); + + /* PRI_B_CONFIG_FD_ALGO_RUN */ + mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003); + + /* PRI_A_CONFIG_FD_ALGO_RUN */ + mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003); + + /* + * AFD range detection tuning registers. + */ + + /* Search_f1_50 */ + mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25); + + /* Search_f2_50 */ + mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28); + + /* Search_f1_60 */ + mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C); + + /* Search_f2_60 */ + mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F); + + /* Period_50Hz (A) */ + mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA); + + /* Secret register by Aptina. */ + /* Period_50Hz (A MSB) */ + mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00); + + /* Period_60Hz (A) */ + mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B); + + /* Secret register by Aptina. */ + /* Period_60Hz (A MSB) */ + mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00); + + /* Period_50Hz (B) */ + mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82); + + /* Secret register by Aptina. */ + /* Period_50Hz (B) MSB */ + mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00); + + /* Period_60Hz (B) */ + mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D); + + /* Secret register by Aptina. */ + /* Period_60Hz (B) MSB */ + mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00); + + /* FD Mode */ + mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10); + + /* Stat_min */ + mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02); + + /* Stat_max */ + mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03); + + /* Min_amplitude */ + mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A); + + /* RX FIFO Watermark (A) */ + mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014); + + /* RX FIFO Watermark (B) */ + mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014); + + /* MCLK: 16MHz + * PCLK: 73MHz + * CorePixCLK: 36.5 MHz + */ + mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133); + mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110); + mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130); + mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108); + + mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27); + mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30); + mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32); + mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35); + + return ret; +} + +static int mt9t112_auto_focus_setting(const struct i2c_client *client) +{ + int ret; + + mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F); + mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F); + mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); + + mt9t112_reg_write(ret, client, 0x0614, 0x0000); + + mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); + mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02); + mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002); + mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001); + mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025); + mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193); + mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18); + mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); + + return ret; +} + +static int mt9t112_auto_focus_trigger(const struct i2c_client *client) +{ + int ret; + + mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01); + + return ret; +} + +static int mt9t112_init_camera(const struct i2c_client *client) +{ + int ret; + + ECHECKER(ret, mt9t112_reset(client)); + ECHECKER(ret, mt9t112_init_pll(client)); + ECHECKER(ret, mt9t112_init_setting(client)); + ECHECKER(ret, mt9t112_auto_focus_setting(client)); + + mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0); + + /* Analog setting B.*/ + mt9t112_reg_write(ret, client, 0x3084, 0x2409); + mt9t112_reg_write(ret, client, 0x3092, 0x0A49); + mt9t112_reg_write(ret, client, 0x3094, 0x4949); + mt9t112_reg_write(ret, client, 0x3096, 0x4950); + + /* + * Disable adaptive clock. + * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR + * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR + */ + mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E); + mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E); + + /* + * Configure Status in Status_before_length Format and enable header. + * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR + */ + mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4); + + /* + * Enable JPEG in context B. + * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR + */ + mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01); + + /* Disable Dac_TXLO. */ + mt9t112_reg_write(ret, client, 0x316C, 0x350F); + + /* Set max slew rates. */ + mt9t112_reg_write(ret, client, 0x1E, 0x777); + + return ret; +} + +/************************************************************************ + * v4l2_subdev_core_ops + ***********************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9t112_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + reg->size = 2; + mt9t112_reg_read(ret, client, reg->reg); + + reg->val = (__u64)ret; + + return 0; +} + +static int mt9t112_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + mt9t112_reg_write(ret, client, reg->reg, reg->val); + + return ret; +} +#endif + +static int mt9t112_power_on(struct mt9t112_priv *priv) +{ + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + if (priv->standby_gpio) { + gpiod_set_value(priv->standby_gpio, 0); + msleep(100); + } + + return 0; +} + +static int mt9t112_power_off(struct mt9t112_priv *priv) +{ + clk_disable_unprepare(priv->clk); + if (priv->standby_gpio) { + gpiod_set_value(priv->standby_gpio, 1); + msleep(100); + } + + return 0; +} + +static int mt9t112_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + return on ? mt9t112_power_on(priv) : + mt9t112_power_off(priv); +} + +static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9t112_g_register, + .s_register = mt9t112_s_register, +#endif + .s_power = mt9t112_s_power, +}; + +/************************************************************************ + * v4l2_subdev_video_ops + **********************************************************************/ +static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + int ret = 0; + + if (!enable) { + /* FIXME + * + * If user selected large output size, and used it long time, + * mt9t112 camera will be very warm. + * + * But current driver can not stop mt9t112 camera. + * So, set small size here to solve this problem. + */ + mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT); + return ret; + } + + if (!priv->init_done) { + u16 param = MT9T112_FLAG_PCLK_RISING_EDGE & priv->info->flags ? + 0x0001 : 0x0000; + + ECHECKER(ret, mt9t112_init_camera(client)); + + /* Invert PCLK (Data sampled on falling edge of pixclk). */ + mt9t112_reg_write(ret, client, 0x3C20, param); + + mdelay(5); + + priv->init_done = true; + } + + mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt); + mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order); + mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); + + mt9t112_set_a_frame_size(client, priv->frame.width, priv->frame.height); + + ECHECKER(ret, mt9t112_auto_focus_trigger(client)); + + dev_dbg(&client->dev, "format : %d\n", priv->format->code); + dev_dbg(&client->dev, "size : %d x %d\n", + priv->frame.width, + priv->frame.height); + + CLOCK_INFO(client, EXT_CLOCK); + + return ret; +} + +static int mt9t112_set_params(struct mt9t112_priv *priv, + const struct v4l2_rect *rect, + u32 code) +{ + int i; + + /* + * get color format + */ + for (i = 0; i < priv->num_formats; i++) + if (mt9t112_cfmts[i].code == code) + break; + + if (i == priv->num_formats) + return -EINVAL; + + priv->frame = *rect; + + /* + * frame size check + */ + v4l_bound_align_image(&priv->frame.width, 0, MAX_WIDTH, 0, + &priv->frame.height, 0, MAX_HEIGHT, 0, 0); + + priv->format = mt9t112_cfmts + i; + + return 0; +} + +static int mt9t112_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = MAX_WIDTH; + sel->r.height = MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = VGA_WIDTH; + sel->r.height = VGA_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->frame; + return 0; + default: + return -EINVAL; + } +} + +static int mt9t112_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + const struct v4l2_rect *rect = &sel->r; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + return mt9t112_set_params(priv, rect, priv->format->code); +} + +static int mt9t112_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (format->pad) + return -EINVAL; + + mf->width = priv->frame.width; + mf->height = priv->frame.height; + mf->colorspace = priv->format->colorspace; + mf->code = priv->format->code; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int mt9t112_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + struct v4l2_rect rect = { + .width = mf->width, + .height = mf->height, + .left = priv->frame.left, + .top = priv->frame.top, + }; + int ret; + + ret = mt9t112_set_params(priv, &rect, mf->code); + + if (!ret) + mf->colorspace = priv->format->colorspace; + + return ret; +} + +static int mt9t112_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + struct mt9t112_priv *priv = to_mt9t112(client); + int i; + + if (format->pad) + return -EINVAL; + + for (i = 0; i < priv->num_formats; i++) + if (mt9t112_cfmts[i].code == mf->code) + break; + + if (i == priv->num_formats) { + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_JPEG; + } else { + mf->colorspace = mt9t112_cfmts[i].colorspace; + } + + v4l_bound_align_image(&mf->width, 0, MAX_WIDTH, 0, + &mf->height, 0, MAX_HEIGHT, 0, 0); + + mf->field = V4L2_FIELD_NONE; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9t112_s_fmt(sd, mf); + cfg->try_fmt = *mf; + + return 0; +} + +static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (code->pad || code->index >= priv->num_formats) + return -EINVAL; + + code->code = mt9t112_cfmts[code->index].code; + + return 0; +} + +static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { + .s_stream = mt9t112_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { + .enum_mbus_code = mt9t112_enum_mbus_code, + .get_selection = mt9t112_get_selection, + .set_selection = mt9t112_set_selection, + .get_fmt = mt9t112_get_fmt, + .set_fmt = mt9t112_set_fmt, +}; + +/************************************************************************ + * i2c driver + ***********************************************************************/ +static const struct v4l2_subdev_ops mt9t112_subdev_ops = { + .core = &mt9t112_subdev_core_ops, + .video = &mt9t112_subdev_video_ops, + .pad = &mt9t112_subdev_pad_ops, +}; + +static int mt9t112_camera_probe(struct i2c_client *client) +{ + struct mt9t112_priv *priv = to_mt9t112(client); + const char *devname; + int chipid; + int ret; + + ret = mt9t112_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* Check and show chip ID. */ + mt9t112_reg_read(chipid, client, 0x0000); + + switch (chipid) { + case 0x2680: + devname = "mt9t111"; + priv->num_formats = 1; + break; + case 0x2682: + devname = "mt9t112"; + priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); + break; + default: + dev_err(&client->dev, "Product ID error %04x\n", chipid); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid); + +done: + mt9t112_s_power(&priv->subdev, 0); + + return ret; +} + +static int mt9t112_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t112_priv *priv; + int ret; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "mt9t112: missing platform data!\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = client->dev.platform_data; + priv->init_done = false; + + v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); + + priv->clk = devm_clk_get(&client->dev, "extclk"); + if (PTR_ERR(priv->clk) == -ENOENT) { + priv->clk = NULL; + } else if (IS_ERR(priv->clk)) { + dev_err(&client->dev, "Unable to get clock \"extclk\"\n"); + return PTR_ERR(priv->clk); + } + + priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->standby_gpio)) { + dev_err(&client->dev, "Unable to get gpio \"standby\"\n"); + return PTR_ERR(priv->standby_gpio); + } + + ret = mt9t112_camera_probe(client); + if (ret) + return ret; + + return v4l2_async_register_subdev(&priv->subdev); +} + +static int mt9t112_remove(struct i2c_client *client) +{ + struct mt9t112_priv *priv = to_mt9t112(client); + + clk_disable_unprepare(priv->clk); + v4l2_async_unregister_subdev(&priv->subdev); + + return 0; +} + +static const struct i2c_device_id mt9t112_id[] = { + { "mt9t112", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t112_id); + +static struct i2c_driver mt9t112_i2c_driver = { + .driver = { + .name = "mt9t112", + }, + .probe = mt9t112_probe, + .remove = mt9t112_remove, + .id_table = mt9t112_id, +}; + +module_i2c_driver(mt9t112_i2c_driver); + +MODULE_DESCRIPTION("V4L2 driver for MT9T111/MT9T112 camera sensor"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 5e29064fae91..46ef74a2ca36 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -364,33 +364,22 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd, return 0; } -static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int mt9v011_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; calc_fps(sd, - &cp->timeperframe.numerator, - &cp->timeperframe.denominator); + &ival->interval.numerator, + &ival->interval.denominator); return 0; } -static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int mt9v011_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; + struct v4l2_fract *tpf = &ival->interval; u16 speed; - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - speed = calc_speed(sd, tpf->numerator, tpf->denominator); mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); @@ -469,8 +458,8 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = { }; static const struct v4l2_subdev_video_ops mt9v011_video_ops = { - .g_parm = mt9v011_g_parm, - .s_parm = mt9v011_s_parm, + .g_frame_interval = mt9v011_g_frame_interval, + .s_frame_interval = mt9v011_s_frame_interval, }; static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = { diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index bf7d06f3f21a..30ee9f71bf0d 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -194,6 +194,7 @@ static const struct ov13858_reg mode_4224x3136_regs[] = { {0x3624, 0x1c}, {0x3640, 0x10}, {0x3641, 0x70}, + {0x3660, 0x04}, {0x3661, 0x80}, {0x3662, 0x12}, {0x3664, 0x73}, @@ -384,6 +385,7 @@ static const struct ov13858_reg mode_2112x1568_regs[] = { {0x3624, 0x1c}, {0x3640, 0x10}, {0x3641, 0x70}, + {0x3660, 0x04}, {0x3661, 0x80}, {0x3662, 0x10}, {0x3664, 0x73}, @@ -574,6 +576,7 @@ static const struct ov13858_reg mode_2112x1188_regs[] = { {0x3624, 0x1c}, {0x3640, 0x10}, {0x3641, 0x70}, + {0x3660, 0x04}, {0x3661, 0x80}, {0x3662, 0x10}, {0x3664, 0x73}, @@ -764,6 +767,7 @@ static const struct ov13858_reg mode_1056x784_regs[] = { {0x3624, 0x1c}, {0x3640, 0x10}, {0x3641, 0x70}, + {0x3660, 0x04}, {0x3661, 0x80}, {0x3662, 0x08}, {0x3664, 0x73}, @@ -1057,14 +1061,15 @@ struct ov13858 { #define to_ov13858(_sd) container_of(_sd, struct ov13858, sd) /* Read registers up to 4 at a time */ -static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val) +static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, + u32 *val) { struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); struct i2c_msg msgs[2]; u8 *data_be_p; int ret; - u32 data_be = 0; - u16 reg_addr_be = cpu_to_be16(reg); + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); if (len > 4) return -EINVAL; @@ -1092,11 +1097,13 @@ static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val) } /* Write registers up to 4 at a time */ -static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 val) +static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, + u32 __val) { struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); int buf_i, val_i; u8 buf[6], *val_p; + __be32 val; if (len > 4) return -EINVAL; @@ -1104,7 +1111,7 @@ static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 val) buf[0] = reg >> 8; buf[1] = reg & 0xff; - val = cpu_to_be32(val); + val = cpu_to_be32(__val); val_p = (u8 *)&val; buf_i = 2; val_i = 4 - len; @@ -1348,39 +1355,6 @@ static int ov13858_get_pad_format(struct v4l2_subdev *sd, return ret; } -/* - * Calculate resolution distance - */ -static int -ov13858_get_resolution_dist(const struct ov13858_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -/* - * Find the closest supported resolution to the requested resolution - */ -static const struct ov13858_mode * -ov13858_find_best_fit(struct ov13858 *ov13858, - struct v4l2_subdev_format *fmt) -{ - int i, dist, cur_best_fit = 0, cur_best_fit_dist = -1; - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = ov13858_get_resolution_dist(&supported_modes[i], - framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static int ov13858_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, @@ -1401,7 +1375,8 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - mode = ov13858_find_best_fit(ov13858, fmt); + mode = v4l2_find_nearest_size(supported_modes, width, height, + fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); @@ -1565,7 +1540,7 @@ static int __maybe_unused ov13858_resume(struct device *dev) error: ov13858_stop_streaming(ov13858); - ov13858->streaming = 0; + ov13858->streaming = false; return ret; } diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c new file mode 100644 index 000000000000..83c55e8288e7 --- /dev/null +++ b/drivers/media/i2c/ov2685.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov2685 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#define CHIP_ID 0x2685 +#define OV2685_REG_CHIP_ID 0x300a + +#define OV2685_XVCLK_FREQ 24000000 + +#define REG_SC_CTRL_MODE 0x0100 +#define SC_CTRL_MODE_STANDBY 0x0 +#define SC_CTRL_MODE_STREAMING BIT(0) + +#define OV2685_REG_EXPOSURE 0x3500 +#define OV2685_EXPOSURE_MIN 4 +#define OV2685_EXPOSURE_STEP 1 + +#define OV2685_REG_VTS 0x380e +#define OV2685_VTS_MAX 0x7fff + +#define OV2685_REG_GAIN 0x350a +#define OV2685_GAIN_MIN 0 +#define OV2685_GAIN_MAX 0x07ff +#define OV2685_GAIN_STEP 0x1 +#define OV2685_GAIN_DEFAULT 0x0036 + +#define OV2685_REG_TEST_PATTERN 0x5080 +#define OV2685_TEST_PATTERN_DISABLED 0x00 +#define OV2685_TEST_PATTERN_COLOR_BAR 0x80 +#define OV2685_TEST_PATTERN_RANDOM 0x81 +#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88 +#define OV2685_TEST_PATTERN_BW_SQUARE 0x92 +#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82 + +#define REG_NULL 0xFFFF + +#define OV2685_REG_VALUE_08BIT 1 +#define OV2685_REG_VALUE_16BIT 2 +#define OV2685_REG_VALUE_24BIT 3 + +#define OV2685_LANES 1 +#define OV2685_BITS_PER_SAMPLE 10 + +static const char * const ov2685_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV2685_NUM_SUPPLIES ARRAY_SIZE(ov2685_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov2685_mode { + u32 width; + u32 height; + u32 exp_def; + u32 hts_def; + u32 vts_def; + const struct regval *reg_list; +}; + +struct ov2685 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[OV2685_NUM_SUPPLIES]; + + bool streaming; + struct mutex mutex; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl_handler ctrl_handler; + + const struct ov2685_mode *cur_mode; +}; + +#define to_ov2685(sd) container_of(sd, struct ov2685, subdev) + +/* PLL settings bases on 24M xvclk */ +static struct regval ov2685_1600x1200_regs[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x1c}, + {0x3018, 0x44}, + {0x301d, 0xf0}, + {0x3020, 0x00}, + {0x3082, 0x37}, + {0x3083, 0x03}, + {0x3084, 0x09}, + {0x3085, 0x04}, + {0x3086, 0x00}, + {0x3087, 0x00}, + {0x3501, 0x4e}, + {0x3502, 0xe0}, + {0x3503, 0x27}, + {0x350b, 0x36}, + {0x3600, 0xb4}, + {0x3603, 0x35}, + {0x3604, 0x24}, + {0x3605, 0x00}, + {0x3620, 0x24}, + {0x3621, 0x34}, + {0x3622, 0x03}, + {0x3628, 0x10}, + {0x3705, 0x3c}, + {0x370a, 0x21}, + {0x370c, 0x50}, + {0x370d, 0xc0}, + {0x3717, 0x58}, + {0x3718, 0x80}, + {0x3720, 0x00}, + {0x3721, 0x09}, + {0x3722, 0x06}, + {0x3723, 0x59}, + {0x3738, 0x99}, + {0x3781, 0x80}, + {0x3784, 0x0c}, + {0x3789, 0x60}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x06}, + {0x3805, 0x4f}, + {0x3806, 0x04}, + {0x3807, 0xbf}, + {0x3808, 0x06}, + {0x3809, 0x40}, + {0x380a, 0x04}, + {0x380b, 0xb0}, + {0x380c, 0x06}, + {0x380d, 0xa4}, + {0x380e, 0x05}, + {0x380f, 0x0e}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3819, 0x04}, + {0x3820, 0xc0}, + {0x3821, 0x00}, + {0x3a06, 0x01}, + {0x3a07, 0x84}, + {0x3a08, 0x01}, + {0x3a09, 0x43}, + {0x3a0a, 0x24}, + {0x3a0b, 0x60}, + {0x3a0c, 0x28}, + {0x3a0d, 0x60}, + {0x3a0e, 0x04}, + {0x3a0f, 0x8c}, + {0x3a10, 0x05}, + {0x3a11, 0x0c}, + {0x4000, 0x81}, + {0x4001, 0x40}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {0x4300, 0x00}, + {0x430e, 0x00}, + {0x4602, 0x02}, + {0x481b, 0x40}, + {0x481f, 0x40}, + {0x4837, 0x18}, + {0x5000, 0x1f}, + {0x5001, 0x05}, + {0x5002, 0x30}, + {0x5003, 0x04}, + {0x5004, 0x00}, + {0x5005, 0x0c}, + {0x5280, 0x15}, + {0x5281, 0x06}, + {0x5282, 0x06}, + {0x5283, 0x08}, + {0x5284, 0x1c}, + {0x5285, 0x1c}, + {0x5286, 0x20}, + {0x5287, 0x10}, + {REG_NULL, 0x00} +}; + +#define OV2685_LINK_FREQ_330MHZ 330000000 +static const s64 link_freq_menu_items[] = { + OV2685_LINK_FREQ_330MHZ +}; + +static const char * const ov2685_test_pattern_menu[] = { + "Disabled", + "Color Bar", + "Color Bar FADE", + "Random Data", + "Black White Square", + "Color Square" +}; + +static const int ov2685_test_pattern_val[] = { + OV2685_TEST_PATTERN_DISABLED, + OV2685_TEST_PATTERN_COLOR_BAR, + OV2685_TEST_PATTERN_COLOR_BAR_FADE, + OV2685_TEST_PATTERN_RANDOM, + OV2685_TEST_PATTERN_BW_SQUARE, + OV2685_TEST_PATTERN_COLOR_SQUARE, +}; + +static const struct ov2685_mode supported_modes[] = { + { + .width = 1600, + .height = 1200, + .exp_def = 0x04ee, + .hts_def = 0x06a4, + .vts_def = 0x050e, + .reg_list = ov2685_1600x1200_regs, + }, +}; + +/* Write registers up to 4 at a time */ +static int ov2685_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 val_i, buf_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov2685_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int ret = 0; + u32 i; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov2685_write_reg(client, regs[i].addr, + OV2685_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov2685_read_reg(struct i2c_client *client, u16 reg, + u32 len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static void ov2685_fill_fmt(const struct ov2685_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov2685_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + + /* only one mode supported for now */ + ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt); + + return 0; +} + +static int ov2685_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + + ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt); + + return 0; +} + +static int ov2685_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + int index = fse->index; + + if (index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fse->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + fse->min_width = supported_modes[index].width; + fse->max_width = supported_modes[index].width; + fse->max_height = supported_modes[index].height; + fse->min_height = supported_modes[index].height; + + return 0; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov2685_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV2685_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov2685_power_on(struct ov2685 *ov2685) +{ + int ret; + u32 delay_us; + struct device *dev = &ov2685->client->dev; + + ret = clk_prepare_enable(ov2685->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + gpiod_set_value_cansleep(ov2685->reset_gpio, 1); + + ret = regulator_bulk_enable(OV2685_NUM_SUPPLIES, ov2685->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + /* The minimum delay between power supplies and reset rising can be 0 */ + gpiod_set_value_cansleep(ov2685->reset_gpio, 0); + /* 8192 xvclk cycles prior to the first SCCB transaction */ + delay_us = ov2685_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + /* HACK: ov2685 would output messy data after reset(R0103), + * writing register before .s_stream() as a workaround + */ + ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list); + if (ret) + goto disable_supplies; + + return 0; + +disable_supplies: + regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies); +disable_clk: + clk_disable_unprepare(ov2685->xvclk); + + return ret; +} + +static void __ov2685_power_off(struct ov2685 *ov2685) +{ + /* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */ + u32 delay_us = ov2685_cal_delay(512); + + usleep_range(delay_us, delay_us * 2); + clk_disable_unprepare(ov2685->xvclk); + gpiod_set_value_cansleep(ov2685->reset_gpio, 1); + regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies); +} + +static int ov2685_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + struct i2c_client *client = ov2685->client; + int ret = 0; + + mutex_lock(&ov2685->mutex); + + on = !!on; + if (on == ov2685->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&ov2685->client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ret = __v4l2_ctrl_handler_setup(&ov2685->ctrl_handler); + if (ret) { + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + ret = ov2685_write_reg(client, REG_SC_CTRL_MODE, + OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING); + if (ret) { + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + ov2685_write_reg(client, REG_SC_CTRL_MODE, + OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY); + pm_runtime_put(&ov2685->client->dev); + } + + ov2685->streaming = on; + +unlock_and_return: + mutex_unlock(&ov2685->mutex); + + return ret; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + struct v4l2_mbus_framefmt *try_fmt; + + mutex_lock(&ov2685->mutex); + + try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); + /* Initialize try_fmt */ + ov2685_fill_fmt(&supported_modes[0], try_fmt); + + mutex_unlock(&ov2685->mutex); + + return 0; +} +#endif + +static int __maybe_unused ov2685_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2685 *ov2685 = to_ov2685(sd); + + return __ov2685_power_on(ov2685); +} + +static int __maybe_unused ov2685_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2685 *ov2685 = to_ov2685(sd); + + __ov2685_power_off(ov2685); + + return 0; +} + +static const struct dev_pm_ops ov2685_pm_ops = { + SET_RUNTIME_PM_OPS(ov2685_runtime_suspend, + ov2685_runtime_resume, NULL) +}; + +static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov2685 *ov2685 = container_of(ctrl->handler, + struct ov2685, ctrl_handler); + struct i2c_client *client = ov2685->client; + s64 max_expo; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max_expo = ov2685->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov2685->exposure, + ov2685->exposure->minimum, max_expo, + ov2685->exposure->step, + ov2685->exposure->default_value); + break; + } + + if (pm_runtime_get_if_in_use(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE, + OV2685_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov2685_write_reg(ov2685->client, OV2685_REG_GAIN, + OV2685_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ov2685_write_reg(ov2685->client, OV2685_REG_VTS, + OV2685_REG_VALUE_16BIT, + ctrl->val + ov2685->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN, + OV2685_REG_VALUE_08BIT, + ov2685_test_pattern_val[ctrl->val]); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + ret = -EINVAL; + break; + }; + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov2685_video_ops = { + .s_stream = ov2685_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov2685_pad_ops = { + .enum_mbus_code = ov2685_enum_mbus_code, + .enum_frame_size = ov2685_enum_frame_sizes, + .get_fmt = ov2685_get_fmt, + .set_fmt = ov2685_set_fmt, +}; + +static const struct v4l2_subdev_ops ov2685_subdev_ops = { + .video = &ov2685_video_ops, + .pad = &ov2685_pad_ops, +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov2685_internal_ops = { + .open = ov2685_open, +}; +#endif + +static const struct v4l2_ctrl_ops ov2685_ctrl_ops = { + .s_ctrl = ov2685_set_ctrl, +}; + +static int ov2685_initialize_controls(struct ov2685 *ov2685) +{ + const struct ov2685_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + u64 exposure_max; + u32 pixel_rate, h_blank; + int ret; + + handler = &ov2685->ctrl_handler; + mode = ov2685->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov2685->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) / + OV2685_BITS_PER_SAMPLE; + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, pixel_rate, 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov2685->hblank) + ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops, + V4L2_CID_VBLANK, mode->vts_def - mode->height, + OV2685_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + + exposure_max = mode->vts_def - 4; + ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops, + V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN, + exposure_max, OV2685_EXPOSURE_STEP, + mode->exp_def); + + ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN, + OV2685_GAIN_MAX, OV2685_GAIN_STEP, + OV2685_GAIN_DEFAULT); + + ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2685_test_pattern_menu) - 1, + 0, 0, ov2685_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov2685->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov2685->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov2685_check_sensor_id(struct ov2685 *ov2685, + struct i2c_client *client) +{ + struct device *dev = &ov2685->client->dev; + int ret; + u32 id = 0; + + ret = ov2685_read_reg(client, OV2685_REG_CHIP_ID, + OV2685_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); + return ret; + } + + dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID); + + return 0; +} + +static int ov2685_configure_regulators(struct ov2685 *ov2685) +{ + int i; + + for (i = 0; i < OV2685_NUM_SUPPLIES; i++) + ov2685->supplies[i].supply = ov2685_supply_names[i]; + + return devm_regulator_bulk_get(&ov2685->client->dev, + OV2685_NUM_SUPPLIES, + ov2685->supplies); +} + +static int ov2685_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ov2685 *ov2685; + int ret; + + ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL); + if (!ov2685) + return -ENOMEM; + + ov2685->client = client; + ov2685->cur_mode = &supported_modes[0]; + + ov2685->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov2685->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + ret = clk_set_rate(ov2685->xvclk, OV2685_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + + ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov2685->reset_gpio)) { + dev_err(dev, "Failed to get reset-gpios\n"); + return -EINVAL; + } + + ret = ov2685_configure_regulators(ov2685); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov2685->mutex); + v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops); + ret = ov2685_initialize_controls(ov2685); + if (ret) + goto err_destroy_mutex; + + ret = __ov2685_power_on(ov2685); + if (ret) + goto err_free_handler; + + ret = ov2685_check_sensor_id(ov2685, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + ov2685->subdev.internal_ops = &ov2685_internal_ops; + ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov2685->pad.flags = MEDIA_PAD_FL_SOURCE; + ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad); + if (ret < 0) + goto err_power_off; +#endif + + ret = v4l2_async_register_subdev(&ov2685->subdev); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&ov2685->subdev.entity); +#endif +err_power_off: + __ov2685_power_off(ov2685); +err_free_handler: + v4l2_ctrl_handler_free(&ov2685->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov2685->mutex); + + return ret; +} + +static int ov2685_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2685 *ov2685 = to_ov2685(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov2685->ctrl_handler); + mutex_destroy(&ov2685->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov2685_power_off(ov2685); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov2685_of_match[] = { + { .compatible = "ovti,ov2685" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov2685_of_match); +#endif + +static struct i2c_driver ov2685_i2c_driver = { + .driver = { + .name = "ov2685", + .owner = THIS_MODULE, + .pm = &ov2685_pm_ops, + .of_match_table = of_match_ptr(ov2685_of_match), + }, + .probe = &ov2685_probe, + .remove = &ov2685_remove, +}; + +module_i2c_driver(ov2685_i2c_driver); + +MODULE_DESCRIPTION("OmniVision ov2685 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index e2dd352224c7..03940f0cdfa6 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -14,14 +14,14 @@ #include <linux/ctype.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/gpio/consumer.h> -#include <linux/regulator/consumer.h> #include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -34,6 +34,8 @@ #define OV5640_DEFAULT_SLAVE_ID 0x3c +#define OV5640_REG_SYS_RESET02 0x3002 +#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006 #define OV5640_REG_SYS_CTRL0 0x3008 #define OV5640_REG_CHIP_ID 0x300a #define OV5640_REG_IO_MIPI_CTRL00 0x300e @@ -114,6 +116,7 @@ struct ov5640_pixfmt { }; static const struct ov5640_pixfmt ov5640_formats[] = { + { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, }, { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, @@ -125,7 +128,7 @@ static const struct ov5640_pixfmt ov5640_formats[] = { * to set the MIPI CSI-2 virtual channel. */ static unsigned int virtual_channel; -module_param(virtual_channel, int, 0); +module_param(virtual_channel, uint, 0444); MODULE_PARM_DESC(virtual_channel, "MIPI CSI-2 virtual channel (0..3), default 0"); @@ -136,7 +139,7 @@ static const int ov5640_framerates[] = { /* regulator supplies */ static const char * const ov5640_supply_name[] = { - "DOVDD", /* Digital I/O (1.8V) suppply */ + "DOVDD", /* Digital I/O (1.8V) supply */ "DVDD", /* Digital Core (1.5V) supply */ "AVDD", /* Analog (2.8V) supply */ }; @@ -242,7 +245,6 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) */ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { - {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, @@ -331,7 +333,6 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { }; static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, @@ -374,7 +375,6 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { }; static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, @@ -481,6 +481,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; + static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, @@ -836,7 +837,7 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val) ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) { - v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n", + dev_err(&client->dev, "%s: error: reg=%x, val=%x\n", __func__, reg, val); return ret; } @@ -865,8 +866,11 @@ static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val) msg[1].len = 1; ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) + if (ret < 0) { + dev_err(&client->dev, "%s: error: reg=%x\n", + __func__, reg); return ret; + } *val = buf[0]; return 0; @@ -880,7 +884,7 @@ static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val) ret = ov5640_read_reg(sensor, reg, &hi); if (ret) return ret; - ret = ov5640_read_reg(sensor, reg+1, &lo); + ret = ov5640_read_reg(sensor, reg + 1, &lo); if (ret) return ret; @@ -941,7 +945,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, break; if (delay_ms) - usleep_range(1000*delay_ms, 1000*delay_ms+100); + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } return ret; @@ -1283,7 +1287,6 @@ static int ov5640_set_bandingfilter(struct ov5640_dev *sensor) return ret; prev_vts = ret; - /* calculate banding filter */ /* 60Hz */ band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120; @@ -1355,11 +1358,16 @@ static int ov5640_binning_on(struct ov5640_dev *sensor) static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) { + struct i2c_client *client = sensor->i2c_client; u8 temp, channel = virtual_channel; int ret; - if (channel > 3) + if (channel > 3) { + dev_err(&client->dev, + "%s: wrong virtual_channel parameter, expected (0..3), got %d\n", + __func__, channel); return -EINVAL; + } ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp); if (ret) @@ -1399,8 +1407,8 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, * sensor changes between scaling and subsampling, go through * exposure calculation */ -static int ov5640_set_mode_exposure_calc( - struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) +static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) { u32 prev_shutter, prev_gain16; u32 cap_shutter, cap_gain16; @@ -1410,7 +1418,7 @@ static int ov5640_set_mode_exposure_calc( u8 average; int ret; - if (mode->reg_data == NULL) + if (!mode->reg_data) return -EINVAL; /* read preview shutter */ @@ -1564,7 +1572,7 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, { int ret; - if (mode->reg_data == NULL) + if (!mode->reg_data) return -EINVAL; /* Write capture setting */ @@ -1915,6 +1923,7 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, { int ret = 0; bool is_rgb = false; + bool is_jpeg = false; u8 val; switch (format->code) { @@ -1936,6 +1945,11 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, val = 0x61; is_rgb = true; break; + case MEDIA_BUS_FMT_JPEG_1X8: + /* YUV422, YUYV */ + val = 0x30; + is_jpeg = true; + break; default: return -EINVAL; } @@ -1946,8 +1960,40 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - return ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, - is_rgb ? 0x01 : 0x00); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, + is_rgb ? 0x01 : 0x00); + if (ret) + return ret; + + /* + * TIMING TC REG21: + * - [5]: JPEG enable + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, + BIT(5), is_jpeg ? BIT(5) : 0); + if (ret) + return ret; + + /* + * SYSTEM RESET02: + * - [4]: Reset JFIFO + * - [3]: Reset SFIFO + * - [2]: Reset JPEG + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02, + BIT(4) | BIT(3) | BIT(2), + is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2))); + if (ret) + return ret; + + /* + * CLOCK ENABLE02: + * - [5]: Enable JPEG 2x clock + * - [3]: Enable JPEG clock + */ + return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02, + BIT(5) | BIT(3), + is_jpeg ? (BIT(5) | BIT(3)) : 0); } /* @@ -2073,7 +2119,8 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, int auto_gain) if (ctrls->auto_gain->is_new) { ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, - BIT(1), ctrls->auto_gain->val ? 0 : BIT(1)); + BIT(1), + ctrls->auto_gain->val ? 0 : BIT(1)); if (ret) return ret; } @@ -2253,10 +2300,12 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= OV5640_NUM_MODES) return -EINVAL; - fse->min_width = fse->max_width = + fse->min_width = ov5640_mode_data[0][fse->index].width; - fse->min_height = fse->max_height = + fse->max_width = fse->min_width; + fse->min_height = ov5640_mode_data[0][fse->index].height; + fse->max_height = fse->min_height; return 0; } @@ -2325,6 +2374,8 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; + sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width, + mode->height, true); sensor->pending_mode_change = true; out: mutex_unlock(&sensor->lock); @@ -2332,8 +2383,8 @@ out: } static int ov5640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { if (code->pad != 0) return -EINVAL; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 9f9196568eb8..d2db480da1b9 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -1853,8 +1853,8 @@ static int ov5670_read_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); struct i2c_msg msgs[2]; u8 *data_be_p; - u32 data_be = 0; - u16 reg_addr_be = cpu_to_be16(reg); + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); int ret; if (len > 4) @@ -1891,6 +1891,7 @@ static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, int val_i; u8 buf[6]; u8 *val_p; + __be32 tmp; if (len > 4) return -EINVAL; @@ -1898,8 +1899,8 @@ static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len, buf[0] = reg >> 8; buf[1] = reg & 0xff; - val = cpu_to_be32(val); - val_p = (u8 *)&val; + tmp = cpu_to_be32(val); + val_p = (u8 *)&tmp; buf_i = 2; val_i = 4 - len; @@ -2180,36 +2181,6 @@ static int ov5670_enum_frame_size(struct v4l2_subdev *sd, return 0; } -/* Calculate resolution distance */ -static int ov5670_get_reso_dist(const struct ov5670_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -/* Find the closest supported resolution to the requested resolution */ -static const struct ov5670_mode *ov5670_find_best_fit( - struct ov5670 *ov5670, - struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - int i; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = ov5670_get_reso_dist(&supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static void ov5670_update_pad_format(const struct ov5670_mode *mode, struct v4l2_subdev_format *fmt) { @@ -2259,7 +2230,8 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - mode = ov5670_find_best_fit(ov5670, fmt); + mode = v4l2_find_nearest_size(supported_modes, width, height, + fmt->format.width, fmt->format.height); ov5670_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c new file mode 100644 index 000000000000..9be38a0a2046 --- /dev/null +++ b/drivers/media/i2c/ov5695.c @@ -0,0 +1,1399 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov5695 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* 45Mhz * 4 Binning */ +#define OV5695_PIXEL_RATE (45 * 1000 * 1000 * 4) +#define OV5695_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x005695 +#define OV5695_REG_CHIP_ID 0x300a + +#define OV5695_REG_CTRL_MODE 0x0100 +#define OV5695_MODE_SW_STANDBY 0x0 +#define OV5695_MODE_STREAMING BIT(0) + +#define OV5695_REG_EXPOSURE 0x3500 +#define OV5695_EXPOSURE_MIN 4 +#define OV5695_EXPOSURE_STEP 1 +#define OV5695_VTS_MAX 0x7fff + +#define OV5695_REG_ANALOG_GAIN 0x3509 +#define ANALOG_GAIN_MIN 0x10 +#define ANALOG_GAIN_MAX 0xf8 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0xf8 + +#define OV5695_REG_DIGI_GAIN_H 0x350a +#define OV5695_REG_DIGI_GAIN_L 0x350b +#define OV5695_DIGI_GAIN_L_MASK 0x3f +#define OV5695_DIGI_GAIN_H_SHIFT 6 +#define OV5695_DIGI_GAIN_MIN 0 +#define OV5695_DIGI_GAIN_MAX (0x4000 - 1) +#define OV5695_DIGI_GAIN_STEP 1 +#define OV5695_DIGI_GAIN_DEFAULT 1024 + +#define OV5695_REG_TEST_PATTERN 0x4503 +#define OV5695_TEST_PATTERN_ENABLE 0x80 +#define OV5695_TEST_PATTERN_DISABLE 0x0 + +#define OV5695_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV5695_REG_VALUE_08BIT 1 +#define OV5695_REG_VALUE_16BIT 2 +#define OV5695_REG_VALUE_24BIT 3 + +#define OV5695_LANES 2 +#define OV5695_BITS_PER_SAMPLE 10 + +static const char * const ov5695_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5695_NUM_SUPPLIES ARRAY_SIZE(ov5695_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct ov5695_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; +}; + +struct ov5695 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + const struct ov5695_mode *cur_mode; +}; + +#define to_ov5695(sd) container_of(sd, struct ov5695, subdev) + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 672(0x2a0) + * framelength 2232(0x8b8) + * grabwindow_width 1296 + * grabwindow_height 972 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_global_regs[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0300, 0x04}, + {0x0301, 0x00}, + {0x0302, 0x69}, + {0x0303, 0x00}, + {0x0304, 0x00}, + {0x0305, 0x01}, + {0x0307, 0x00}, + {0x030b, 0x00}, + {0x030c, 0x00}, + {0x030d, 0x1e}, + {0x030e, 0x04}, + {0x030f, 0x03}, + {0x0312, 0x01}, + {0x3000, 0x00}, + {0x3002, 0xa1}, + {0x3008, 0x00}, + {0x3010, 0x00}, + {0x3022, 0x51}, + {0x3106, 0x15}, + {0x3107, 0x01}, + {0x3108, 0x05}, + {0x3500, 0x00}, + {0x3501, 0x45}, + {0x3502, 0x00}, + {0x3503, 0x08}, + {0x3504, 0x03}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0x10}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3601, 0x55}, + {0x3602, 0x58}, + {0x3614, 0x30}, + {0x3615, 0x77}, + {0x3621, 0x08}, + {0x3624, 0x40}, + {0x3633, 0x0c}, + {0x3634, 0x0c}, + {0x3635, 0x0c}, + {0x3636, 0x0c}, + {0x3638, 0x00}, + {0x3639, 0x00}, + {0x363a, 0x00}, + {0x363b, 0x00}, + {0x363c, 0xff}, + {0x363d, 0xfa}, + {0x3650, 0x44}, + {0x3651, 0x44}, + {0x3652, 0x44}, + {0x3653, 0x44}, + {0x3654, 0x44}, + {0x3655, 0x44}, + {0x3656, 0x44}, + {0x3657, 0x44}, + {0x3660, 0x00}, + {0x3661, 0x00}, + {0x3662, 0x00}, + {0x366a, 0x00}, + {0x366e, 0x0c}, + {0x3673, 0x04}, + {0x3700, 0x14}, + {0x3703, 0x0c}, + {0x3715, 0x01}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x373f, 0xa0}, + {0x3765, 0x20}, + {0x37a1, 0x1d}, + {0x37a8, 0x26}, + {0x37ab, 0x14}, + {0x37c2, 0x04}, + {0x37cb, 0x09}, + {0x37cc, 0x13}, + {0x37cd, 0x1f}, + {0x37ce, 0x1f}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xaf}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x02}, + {0x380d, 0xa0}, + {0x380e, 0x08}, + {0x380f, 0xb8}, + {0x3810, 0x00}, + {0x3811, 0x06}, + {0x3812, 0x00}, + {0x3813, 0x06}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x03}, + {0x3817, 0x01}, + {0x3818, 0x00}, + {0x3819, 0x00}, + {0x381a, 0x00}, + {0x381b, 0x01}, + {0x3820, 0x8b}, + {0x3821, 0x01}, + {0x3c80, 0x08}, + {0x3c82, 0x00}, + {0x3c83, 0x00}, + {0x3c88, 0x00}, + {0x3d85, 0x14}, + {0x3f02, 0x08}, + {0x3f03, 0x10}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {0x404e, 0x20}, + {0x4501, 0x00}, + {0x4502, 0x10}, + {0x4800, 0x00}, + {0x481f, 0x2a}, + {0x4837, 0x13}, + {0x5000, 0x17}, + {0x5780, 0x3e}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x06}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5b00, 0x00}, + {0x5b01, 0x1c}, + {0x5b02, 0x00}, + {0x5b03, 0x7f}, + {0x5b05, 0x6c}, + {0x5e10, 0xfc}, + {0x4010, 0xf1}, + {0x3503, 0x08}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0xf8}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 740(0x2e4) + * framelength 2024(0x7e8) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_2592x1944_regs[] = { + {0x3501, 0x7e}, + {0x366e, 0x18}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xab}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x380c, 0x02}, + {0x380d, 0xe4}, + {0x380e, 0x07}, + {0x380f, 0xe8}, + {0x3811, 0x06}, + {0x3813, 0x08}, + {0x3814, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x3820, 0x88}, + {0x3821, 0x00}, + {0x4501, 0x00}, + {0x4008, 0x04}, + {0x4009, 0x13}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 672(0x2a0) + * framelength 2232(0x8b8) + * grabwindow_width 1920 + * grabwindow_height 1080 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_1920x1080_regs[] = { + {0x3501, 0x45}, + {0x366e, 0x18}, + {0x3800, 0x01}, + {0x3801, 0x50}, + {0x3802, 0x01}, + {0x3803, 0xb8}, + {0x3804, 0x08}, + {0x3805, 0xef}, + {0x3806, 0x05}, + {0x3807, 0xf7}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x380c, 0x02}, + {0x380d, 0xa0}, + {0x380e, 0x08}, + {0x380f, 0xb8}, + {0x3811, 0x06}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x3820, 0x88}, + {0x3821, 0x00}, + {0x4501, 0x00}, + {0x4008, 0x04}, + {0x4009, 0x13}, + {REG_NULL, 0x00} +}; + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 740(0x02e4) + * framelength 1012(0x03f4) + * grabwindow_width 1296 + * grabwindow_height 972 + * max_framerate 60fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_1296x972_regs[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0300, 0x04}, + {0x0301, 0x00}, + {0x0302, 0x69}, + {0x0303, 0x00}, + {0x0304, 0x00}, + {0x0305, 0x01}, + {0x0307, 0x00}, + {0x030b, 0x00}, + {0x030c, 0x00}, + {0x030d, 0x1e}, + {0x030e, 0x04}, + {0x030f, 0x03}, + {0x0312, 0x01}, + {0x3000, 0x00}, + {0x3002, 0xa1}, + {0x3008, 0x00}, + {0x3010, 0x00}, + {0x3016, 0x32}, + {0x3022, 0x51}, + {0x3106, 0x15}, + {0x3107, 0x01}, + {0x3108, 0x05}, + {0x3500, 0x00}, + {0x3501, 0x3e}, + {0x3502, 0x00}, + {0x3503, 0x08}, + {0x3504, 0x03}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0x10}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3601, 0x55}, + {0x3602, 0x58}, + {0x3611, 0x58}, + {0x3614, 0x30}, + {0x3615, 0x77}, + {0x3621, 0x08}, + {0x3624, 0x40}, + {0x3633, 0x0c}, + {0x3634, 0x0c}, + {0x3635, 0x0c}, + {0x3636, 0x0c}, + {0x3638, 0x00}, + {0x3639, 0x00}, + {0x363a, 0x00}, + {0x363b, 0x00}, + {0x363c, 0xff}, + {0x363d, 0xfa}, + {0x3650, 0x44}, + {0x3651, 0x44}, + {0x3652, 0x44}, + {0x3653, 0x44}, + {0x3654, 0x44}, + {0x3655, 0x44}, + {0x3656, 0x44}, + {0x3657, 0x44}, + {0x3660, 0x00}, + {0x3661, 0x00}, + {0x3662, 0x00}, + {0x366a, 0x00}, + {0x366e, 0x0c}, + {0x3673, 0x04}, + {0x3700, 0x14}, + {0x3703, 0x0c}, + {0x3706, 0x24}, + {0x3714, 0x27}, + {0x3715, 0x01}, + {0x3716, 0x00}, + {0x3717, 0x02}, + {0x3733, 0x10}, + {0x3734, 0x40}, + {0x373f, 0xa0}, + {0x3765, 0x20}, + {0x37a1, 0x1d}, + {0x37a8, 0x26}, + {0x37ab, 0x14}, + {0x37c2, 0x04}, + {0x37c3, 0xf0}, + {0x37cb, 0x09}, + {0x37cc, 0x13}, + {0x37cd, 0x1f}, + {0x37ce, 0x1f}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xaf}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x02}, + {0x380d, 0xe4}, + {0x380e, 0x03}, + {0x380f, 0xf4}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x06}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x03}, + {0x3817, 0x01}, + {0x3818, 0x00}, + {0x3819, 0x00}, + {0x381a, 0x00}, + {0x381b, 0x01}, + {0x3820, 0x8b}, + {0x3821, 0x01}, + {0x3c80, 0x08}, + {0x3c82, 0x00}, + {0x3c83, 0x00}, + {0x3c88, 0x00}, + {0x3d85, 0x14}, + {0x3f02, 0x08}, + {0x3f03, 0x10}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {0x404e, 0x20}, + {0x4501, 0x00}, + {0x4502, 0x10}, + {0x4800, 0x00}, + {0x481f, 0x2a}, + {0x4837, 0x13}, + {0x5000, 0x13}, + {0x5780, 0x3e}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x06}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5b00, 0x00}, + {0x5b01, 0x1c}, + {0x5b02, 0x00}, + {0x5b03, 0x7f}, + {0x5b05, 0x6c}, + {0x5e10, 0xfc}, + {0x4010, 0xf1}, + {0x3503, 0x08}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0xf8}, + {0x0100, 0x01}, + {REG_NULL, 0x00} +}; + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 672(0x2a0) + * framelength 2232(0x8b8) + * grabwindow_width 1280 + * grabwindow_height 720 + * max_framerate 30fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_1280x720_regs[] = { + {0x3501, 0x45}, + {0x366e, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x01}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x06}, + {0x3807, 0xaf}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x02}, + {0x380b, 0xd0}, + {0x380c, 0x02}, + {0x380d, 0xa0}, + {0x380e, 0x08}, + {0x380f, 0xb8}, + {0x3811, 0x06}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3816, 0x03}, + {0x3817, 0x01}, + {0x3820, 0x8b}, + {0x3821, 0x01}, + {0x4501, 0x00}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {REG_NULL, 0x00} +}; + +/* + * Xclk 24Mhz + * Pclk 45Mhz + * linelength 672(0x2a0) + * framelength 558(0x22e) + * grabwindow_width 640 + * grabwindow_height 480 + * max_framerate 120fps + * mipi_datarate per lane 840Mbps + */ +static const struct regval ov5695_640x480_regs[] = { + {0x3501, 0x22}, + {0x366e, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa7}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xe0}, + {0x380c, 0x02}, + {0x380d, 0xa0}, + {0x380e, 0x02}, + {0x380f, 0x2e}, + {0x3811, 0x06}, + {0x3813, 0x04}, + {0x3814, 0x07}, + {0x3816, 0x05}, + {0x3817, 0x03}, + {0x3820, 0x8d}, + {0x3821, 0x01}, + {0x4501, 0x00}, + {0x4008, 0x02}, + {0x4009, 0x09}, + {REG_NULL, 0x00} +}; + +static const struct ov5695_mode supported_modes[] = { + { + .width = 2592, + .height = 1944, + .max_fps = 30, + .exp_def = 0x0450, + .hts_def = 0x02e4 * 4, + .vts_def = 0x07e8, + .reg_list = ov5695_2592x1944_regs, + }, + { + .width = 1920, + .height = 1080, + .max_fps = 30, + .exp_def = 0x0450, + .hts_def = 0x02a0 * 4, + .vts_def = 0x08b8, + .reg_list = ov5695_1920x1080_regs, + }, + { + .width = 1296, + .height = 972, + .max_fps = 60, + .exp_def = 0x03e0, + .hts_def = 0x02e4 * 4, + .vts_def = 0x03f4, + .reg_list = ov5695_1296x972_regs, + }, + { + .width = 1280, + .height = 720, + .max_fps = 30, + .exp_def = 0x0450, + .hts_def = 0x02a0 * 4, + .vts_def = 0x08b8, + .reg_list = ov5695_1280x720_regs, + }, + { + .width = 640, + .height = 480, + .max_fps = 120, + .exp_def = 0x0450, + .hts_def = 0x02a0 * 4, + .vts_def = 0x022e, + .reg_list = ov5695_640x480_regs, + }, +}; + +#define OV5695_LINK_FREQ_420MHZ 420000000 +static const s64 link_freq_menu_items[] = { + OV5695_LINK_FREQ_420MHZ +}; + +static const char * const ov5695_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ov5695_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov5695_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov5695_write_reg(client, regs[i].addr, + OV5695_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov5695_get_reso_dist(const struct ov5695_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov5695_mode * +ov5695_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ov5695_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov5695_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + const struct ov5695_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ov5695->mutex); + + mode = ov5695_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov5695->mutex); + return -ENOTTY; +#endif + } else { + ov5695->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov5695->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov5695->vblank, vblank_def, + OV5695_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&ov5695->mutex); + + return 0; +} + +static int ov5695_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + const struct ov5695_mode *mode = ov5695->cur_mode; + + mutex_lock(&ov5695->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov5695->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov5695->mutex); + + return 0; +} + +static int ov5695_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE; + else + val = OV5695_TEST_PATTERN_DISABLE; + + return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN, + OV5695_REG_VALUE_08BIT, val); +} + +static int __ov5695_start_stream(struct ov5695 *ov5695) +{ + int ret; + + ret = ov5695_write_array(ov5695->client, ov5695_global_regs); + if (ret) + return ret; + ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&ov5695->ctrl_handler); + if (ret) + return ret; + + return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE, + OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING); +} + +static int __ov5695_stop_stream(struct ov5695 *ov5695) +{ + return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE, + OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY); +} + +static int ov5695_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + struct i2c_client *client = ov5695->client; + int ret = 0; + + mutex_lock(&ov5695->mutex); + on = !!on; + if (on == ov5695->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov5695_start_stream(ov5695); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov5695_stop_stream(ov5695); + pm_runtime_put(&client->dev); + } + + ov5695->streaming = on; + +unlock_and_return: + mutex_unlock(&ov5695->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov5695_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV5695_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov5695_power_on(struct ov5695 *ov5695) +{ + int ret; + u32 delay_us; + struct device *dev = &ov5695->client->dev; + + ret = clk_prepare_enable(ov5695->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + + ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + gpiod_set_value_cansleep(ov5695->reset_gpio, 0); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov5695_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov5695->xvclk); + + return ret; +} + +static void __ov5695_power_off(struct ov5695 *ov5695) +{ + clk_disable_unprepare(ov5695->xvclk); + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies); +} + +static int __maybe_unused ov5695_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5695 *ov5695 = to_ov5695(sd); + + return __ov5695_power_on(ov5695); +} + +static int __maybe_unused ov5695_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5695 *ov5695 = to_ov5695(sd); + + __ov5695_power_off(ov5695); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov5695 *ov5695 = to_ov5695(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov5695_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov5695->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov5695->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops ov5695_pm_ops = { + SET_RUNTIME_PM_OPS(ov5695_runtime_suspend, + ov5695_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov5695_internal_ops = { + .open = ov5695_open, +}; +#endif + +static const struct v4l2_subdev_video_ops ov5695_video_ops = { + .s_stream = ov5695_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov5695_pad_ops = { + .enum_mbus_code = ov5695_enum_mbus_code, + .enum_frame_size = ov5695_enum_frame_sizes, + .get_fmt = ov5695_get_fmt, + .set_fmt = ov5695_set_fmt, +}; + +static const struct v4l2_subdev_ops ov5695_subdev_ops = { + .video = &ov5695_video_ops, + .pad = &ov5695_pad_ops, +}; + +static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5695 *ov5695 = container_of(ctrl->handler, + struct ov5695, ctrl_handler); + struct i2c_client *client = ov5695->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = ov5695->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov5695->exposure, + ov5695->exposure->minimum, max, + ov5695->exposure->step, + ov5695->exposure->default_value); + break; + } + + if (pm_runtime_get_if_in_use(&client->dev) <= 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE, + OV5695_REG_VALUE_24BIT, ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN, + OV5695_REG_VALUE_08BIT, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L, + OV5695_REG_VALUE_08BIT, + ctrl->val & OV5695_DIGI_GAIN_L_MASK); + ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H, + OV5695_REG_VALUE_08BIT, + ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT); + break; + case V4L2_CID_VBLANK: + ret = ov5695_write_reg(ov5695->client, OV5695_REG_VTS, + OV5695_REG_VALUE_16BIT, + ctrl->val + ov5695->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov5695_enable_test_pattern(ov5695, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + }; + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov5695_ctrl_ops = { + .s_ctrl = ov5695_set_ctrl, +}; + +static int ov5695_initialize_controls(struct ov5695 *ov5695) +{ + const struct ov5695_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &ov5695->ctrl_handler; + mode = ov5695->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &ov5695->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov5695->hblank) + ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV5695_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops, + V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN, + exposure_max, OV5695_EXPOSURE_STEP, + mode->exp_def); + + ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + /* Digital gain */ + ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN, + OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP, + OV5695_DIGI_GAIN_DEFAULT); + + ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5695_test_pattern_menu) - 1, + 0, 0, ov5695_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&ov5695->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov5695->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov5695_check_sensor_id(struct ov5695 *ov5695, + struct i2c_client *client) +{ + struct device *dev = &ov5695->client->dev; + u32 id = 0; + int ret; + + ret = ov5695_read_reg(client, OV5695_REG_CHIP_ID, + OV5695_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return ret; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov5695_configure_regulators(struct ov5695 *ov5695) +{ + int i; + + for (i = 0; i < OV5695_NUM_SUPPLIES; i++) + ov5695->supplies[i].supply = ov5695_supply_names[i]; + + return devm_regulator_bulk_get(&ov5695->client->dev, + OV5695_NUM_SUPPLIES, + ov5695->supplies); +} + +static int ov5695_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ov5695 *ov5695; + struct v4l2_subdev *sd; + int ret; + + ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL); + if (!ov5695) + return -ENOMEM; + + ov5695->client = client; + ov5695->cur_mode = &supported_modes[0]; + + ov5695->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov5695->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + + ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov5695->reset_gpio)) { + dev_err(dev, "Failed to get reset-gpios\n"); + return -EINVAL; + } + + ret = ov5695_configure_regulators(ov5695); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov5695->mutex); + + sd = &ov5695->subdev; + v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops); + ret = ov5695_initialize_controls(ov5695); + if (ret) + goto err_destroy_mutex; + + ret = __ov5695_power_on(ov5695); + if (ret) + goto err_free_handler; + + ret = ov5695_check_sensor_id(ov5695, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov5695_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov5695->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad); + if (ret < 0) + goto err_power_off; +#endif + + ret = v4l2_async_register_subdev(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov5695_power_off(ov5695); +err_free_handler: + v4l2_ctrl_handler_free(&ov5695->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov5695->mutex); + + return ret; +} + +static int ov5695_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5695 *ov5695 = to_ov5695(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov5695->ctrl_handler); + mutex_destroy(&ov5695->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov5695_power_off(ov5695); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5695_of_match[] = { + { .compatible = "ovti,ov5695" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov5695_of_match); +#endif + +static struct i2c_driver ov5695_i2c_driver = { + .driver = { + .name = "ov5695", + .owner = THIS_MODULE, + .pm = &ov5695_pm_ops, + .of_match_table = of_match_ptr(ov5695_of_match), + }, + .probe = &ov5695_probe, + .remove = &ov5695_remove, +}; + +module_i2c_driver(ov5695_i2c_driver); + +MODULE_DESCRIPTION("OmniVision ov5695 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 8975d16b2b24..17a34b4a819d 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -201,7 +201,7 @@ struct ov6650 { struct v4l2_rect rect; /* sensor cropping window */ unsigned long pclk_limit; /* from host */ unsigned long pclk_max; /* from resolution and format */ - struct v4l2_fract tpf; /* as requested with s_parm */ + struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; enum v4l2_colorspace colorspace; }; @@ -723,42 +723,31 @@ static int ov6650_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int ov6650_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_captureparm *cp = &parms->parm.capture; - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(*cp)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, + ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max)); - cp->timeperframe.denominator = FRAME_RATE_MAX; + ival->interval.denominator = FRAME_RATE_MAX; dev_dbg(&client->dev, "Frame interval: %u/%u s\n", - cp->timeperframe.numerator, cp->timeperframe.denominator); + ival->interval.numerator, ival->interval.denominator); return 0; } -static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int ov6650_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; + struct v4l2_fract *tpf = &ival->interval; int div, ret; u8 clkrc; - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cp->extendedmode != 0) - return -EINVAL; - if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ else @@ -921,8 +910,8 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, - .g_parm = ov6650_g_parm, - .s_parm = ov6650_s_parm, + .g_frame_interval = ov6650_g_frame_interval, + .s_frame_interval = ov6650_s_frame_interval, .g_mbus_config = ov6650_g_mbus_config, .s_mbus_config = ov6650_s_mbus_config, }; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 28571de1c2f6..3474ef832c1e 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -21,6 +21,7 @@ #include <linux/gpio/consumer.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-mediabus.h> #include <media/v4l2-image-sizes.h> #include <media/i2c/ov7670.h> @@ -242,6 +243,7 @@ struct ov7670_info { struct clk *clk; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; + unsigned int mbus_config; /* Media bus configuration flags */ int min_width; /* Filter out smaller sizes */ int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ @@ -1014,7 +1016,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *mbus_fmt; #endif - unsigned char com7; + unsigned char com7, com10 = 0; int ret; if (format->pad) @@ -1034,7 +1036,6 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, } ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize); - if (ret) return ret; /* @@ -1045,16 +1046,41 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, */ com7 = ovfmt->regs[0].value; com7 |= wsize->com7_bit; - ov7670_write(sd, REG_COM7, com7); + ret = ov7670_write(sd, REG_COM7, com7); + if (ret) + return ret; + + /* + * Configure the media bus through COM10 register + */ + if (info->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW) + com10 |= COM10_VS_NEG; + if (info->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW) + com10 |= COM10_HREF_REV; + if (info->pclk_hb_disable) + com10 |= COM10_PCLK_HB; + ret = ov7670_write(sd, REG_COM10, com10); + if (ret) + return ret; + /* * Now write the rest of the array. Also store start/stops */ - ov7670_write_array(sd, ovfmt->regs + 1); - ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, - wsize->vstop); - ret = 0; - if (wsize->regs) + ret = ov7670_write_array(sd, ovfmt->regs + 1); + if (ret) + return ret; + + ret = ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + if (ret) + return ret; + + if (wsize->regs) { ret = ov7670_write_array(sd, wsize->regs); + if (ret) + return ret; + } + info->fmt = ovfmt; /* @@ -1067,8 +1093,10 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, * to write it unconditionally, and that will make the frame * rate persistent too. */ - if (ret == 0) - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret) + return ret; + return 0; } @@ -1100,30 +1128,24 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, * Implement G/S_PARM. There is a "high quality" mode we could try * to do someday; for now, we just do the frame rate tweak. */ -static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int ov7670_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; struct ov7670_info *info = to_state(sd); - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - cp->capability = V4L2_CAP_TIMEPERFRAME; - info->devtype->get_framerate(sd, &cp->timeperframe); + info->devtype->get_framerate(sd, &ival->interval); return 0; } -static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int ov7670_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; + struct v4l2_fract *tpf = &ival->interval; struct ov7670_info *info = to_state(sd); - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - cp->capability = V4L2_CAP_TIMEPERFRAME; return info->devtype->set_framerate(sd, tpf); } @@ -1636,8 +1658,8 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { }; static const struct v4l2_subdev_video_ops ov7670_video_ops = { - .s_parm = ov7670_s_parm, - .g_parm = ov7670_g_parm, + .s_frame_interval = ov7670_s_frame_interval, + .g_frame_interval = ov7670_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov7670_pad_ops = { @@ -1698,6 +1720,45 @@ static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info) return 0; } +/* + * ov7670_parse_dt() - Parse device tree to collect mbus configuration + * properties + */ +static int ov7670_parse_dt(struct device *dev, + struct ov7670_info *info) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg; + struct fwnode_handle *ep; + int ret; + + if (!fwnode) + return -EINVAL; + + info->pclk_hb_disable = false; + if (fwnode_property_present(fwnode, "ov7670,pclk-hb-disable")) + info->pclk_hb_disable = true; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg); + if (ret) { + fwnode_handle_put(ep); + return ret; + } + + if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) { + dev_err(dev, "Unsupported media bus type\n"); + fwnode_handle_put(ep); + return ret; + } + info->mbus_config = bus_cfg.bus.parallel.flags; + + return 0; +} + static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1718,7 +1779,13 @@ static int ov7670_probe(struct i2c_client *client, #endif info->clock_speed = 30; /* default: a guess */ - if (client->dev.platform_data) { + + if (dev_fwnode(&client->dev)) { + ret = ov7670_parse_dt(&client->dev, info); + if (ret) + return ret; + + } else if (client->dev.platform_data) { struct ov7670_config *config = client->dev.platform_data; /* @@ -1785,9 +1852,6 @@ static int ov7670_probe(struct i2c_client *client, tpf.denominator = 30; info->devtype->set_framerate(sd, &tpf); - if (info->pclk_hb_disable) - ov7670_write(sd, REG_COM10, COM10_PCLK_HB); - v4l2_ctrl_handler_init(&info->hdl, 10); v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c new file mode 100644 index 000000000000..b62860c89439 --- /dev/null +++ b/drivers/media/i2c/ov772x.c @@ -0,0 +1,1356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov772x Camera Driver + * + * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> + +#include <media/i2c/ov772x.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-subdev.h> + +/* + * register offset + */ +#define GAIN 0x00 /* AGC - Gain control gain setting */ +#define BLUE 0x01 /* AWB - Blue channel gain setting */ +#define RED 0x02 /* AWB - Red channel gain setting */ +#define GREEN 0x03 /* AWB - Green channel gain setting */ +#define COM1 0x04 /* Common control 1 */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define RAVG 0x07 /* V/R Average Level */ +#define AECH 0x08 /* Exposure Value - AEC MSBs */ +#define COM2 0x09 /* Common control 2 */ +#define PID 0x0A /* Product ID Number MSB */ +#define VER 0x0B /* Product ID Number LSB */ +#define COM3 0x0C /* Common control 3 */ +#define COM4 0x0D /* Common control 4 */ +#define COM5 0x0E /* Common control 5 */ +#define COM6 0x0F /* Common control 6 */ +#define AEC 0x10 /* Exposure Value */ +#define CLKRC 0x11 /* Internal clock */ +#define COM7 0x12 /* Common control 7 */ +#define COM8 0x13 /* Common control 8 */ +#define COM9 0x14 /* Common control 9 */ +#define COM10 0x15 /* Common control 10 */ +#define REG16 0x16 /* Register 16 */ +#define HSTART 0x17 /* Horizontal sensor size */ +#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ +#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ +#define VSIZE 0x1A /* Vertical sensor size */ +#define PSHFT 0x1B /* Data format - pixel delay select */ +#define MIDH 0x1C /* Manufacturer ID byte - high */ +#define MIDL 0x1D /* Manufacturer ID byte - low */ +#define LAEC 0x1F /* Fine AEC value */ +#define COM11 0x20 /* Common control 11 */ +#define BDBASE 0x22 /* Banding filter Minimum AEC value */ +#define DBSTEP 0x23 /* Banding filter Maximum Setp */ +#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ +#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ +#define VPT 0x26 /* AGC/AEC Fast mode operating region */ +#define REG28 0x28 /* Register 28 */ +#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ +#define EXHCH 0x2A /* Dummy pixel insert MSB */ +#define EXHCL 0x2B /* Dummy pixel insert LSB */ +#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ +#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ +#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ +#define YAVE 0x2F /* Y/G Channel Average value */ +#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ +#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ +#define HREF 0x32 /* Image start and size control */ +#define DM_LNL 0x33 /* Dummy line low 8 bits */ +#define DM_LNH 0x34 /* Dummy line high 8 bits */ +#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ +#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ +#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ +#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ +#define OFF_B 0x39 /* Analog process B channel offset value */ +#define OFF_R 0x3A /* Analog process R channel offset value */ +#define OFF_GB 0x3B /* Analog process Gb channel offset value */ +#define OFF_GR 0x3C /* Analog process Gr channel offset value */ +#define COM12 0x3D /* Common control 12 */ +#define COM13 0x3E /* Common control 13 */ +#define COM14 0x3F /* Common control 14 */ +#define COM15 0x40 /* Common control 15*/ +#define COM16 0x41 /* Common control 16 */ +#define TGT_B 0x42 /* BLC blue channel target value */ +#define TGT_R 0x43 /* BLC red channel target value */ +#define TGT_GB 0x44 /* BLC Gb channel target value */ +#define TGT_GR 0x45 /* BLC Gr channel target value */ +/* for ov7720 */ +#define LCC0 0x46 /* Lens correction control 0 */ +#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ +#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ +#define LCC3 0x49 /* Lens correction option 3 */ +#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ +#define LCC5 0x4B /* Lens correction option 5 */ +#define LCC6 0x4C /* Lens correction option 6 */ +/* for ov7725 */ +#define LC_CTR 0x46 /* Lens correction control */ +#define LC_XC 0x47 /* X coordinate of lens correction center relative */ +#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ +#define LC_COEF 0x49 /* Lens correction coefficient */ +#define LC_RADI 0x4A /* Lens correction radius */ +#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ +#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ + +#define FIXGAIN 0x4D /* Analog fix gain amplifer */ +#define AREF0 0x4E /* Sensor reference control */ +#define AREF1 0x4F /* Sensor reference current control */ +#define AREF2 0x50 /* Analog reference control */ +#define AREF3 0x51 /* ADC reference control */ +#define AREF4 0x52 /* ADC reference control */ +#define AREF5 0x53 /* ADC reference control */ +#define AREF6 0x54 /* Analog reference control */ +#define AREF7 0x55 /* Analog reference control */ +#define UFIX 0x60 /* U channel fixed value output */ +#define VFIX 0x61 /* V channel fixed value output */ +#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ +#define AWB_CTRL0 0x63 /* AWB control byte 0 */ +#define DSP_CTRL1 0x64 /* DSP control byte 1 */ +#define DSP_CTRL2 0x65 /* DSP control byte 2 */ +#define DSP_CTRL3 0x66 /* DSP control byte 3 */ +#define DSP_CTRL4 0x67 /* DSP control byte 4 */ +#define AWB_BIAS 0x68 /* AWB BLC level clip */ +#define AWB_CTRL1 0x69 /* AWB control 1 */ +#define AWB_CTRL2 0x6A /* AWB control 2 */ +#define AWB_CTRL3 0x6B /* AWB control 3 */ +#define AWB_CTRL4 0x6C /* AWB control 4 */ +#define AWB_CTRL5 0x6D /* AWB control 5 */ +#define AWB_CTRL6 0x6E /* AWB control 6 */ +#define AWB_CTRL7 0x6F /* AWB control 7 */ +#define AWB_CTRL8 0x70 /* AWB control 8 */ +#define AWB_CTRL9 0x71 /* AWB control 9 */ +#define AWB_CTRL10 0x72 /* AWB control 10 */ +#define AWB_CTRL11 0x73 /* AWB control 11 */ +#define AWB_CTRL12 0x74 /* AWB control 12 */ +#define AWB_CTRL13 0x75 /* AWB control 13 */ +#define AWB_CTRL14 0x76 /* AWB control 14 */ +#define AWB_CTRL15 0x77 /* AWB control 15 */ +#define AWB_CTRL16 0x78 /* AWB control 16 */ +#define AWB_CTRL17 0x79 /* AWB control 17 */ +#define AWB_CTRL18 0x7A /* AWB control 18 */ +#define AWB_CTRL19 0x7B /* AWB control 19 */ +#define AWB_CTRL20 0x7C /* AWB control 20 */ +#define AWB_CTRL21 0x7D /* AWB control 21 */ +#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ +#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ +#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ +#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ +#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ +#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ +#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ +#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ +#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ +#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ +#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ +#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ +#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ +#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ +#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ +#define SLOP 0x8D /* Gamma curve highest segment slope */ +#define DNSTH 0x8E /* De-noise threshold */ +#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ +#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ +#define DNSOFF 0x91 /* Auto De-noise threshold control */ +#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ +#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ +#define MTX1 0x94 /* Matrix coefficient 1 */ +#define MTX2 0x95 /* Matrix coefficient 2 */ +#define MTX3 0x96 /* Matrix coefficient 3 */ +#define MTX4 0x97 /* Matrix coefficient 4 */ +#define MTX5 0x98 /* Matrix coefficient 5 */ +#define MTX6 0x99 /* Matrix coefficient 6 */ +#define MTX_CTRL 0x9A /* Matrix control */ +#define BRIGHT 0x9B /* Brightness control */ +#define CNTRST 0x9C /* Contrast contrast */ +#define CNTRST_CTRL 0x9D /* Contrast contrast center */ +#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ +#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ +#define SCAL0 0xA0 /* Scaling control 0 */ +#define SCAL1 0xA1 /* Scaling control 1 */ +#define SCAL2 0xA2 /* Scaling control 2 */ +#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ +#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ +#define SDE 0xA6 /* Special digital effect control */ +#define USAT 0xA7 /* U component saturation control */ +#define VSAT 0xA8 /* V component saturation control */ +/* for ov7720 */ +#define HUE0 0xA9 /* Hue control 0 */ +#define HUE1 0xAA /* Hue control 1 */ +/* for ov7725 */ +#define HUECOS 0xA9 /* Cosine value */ +#define HUESIN 0xAA /* Sine value */ + +#define SIGN 0xAB /* Sign bit for Hue and contrast */ +#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ + +/* + * register detail + */ + +/* COM2 */ +#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ + /* Output drive capability */ +#define OCAP_1x 0x00 /* 1x */ +#define OCAP_2x 0x01 /* 2x */ +#define OCAP_3x 0x02 /* 3x */ +#define OCAP_4x 0x03 /* 4x */ + +/* COM3 */ +#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) +#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) + +#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ +#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ +#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ +#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ +#define SWAP_ML 0x08 /* Swap output MSB/LSB */ + /* Tri-state option for output clock */ +#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ + /* 1: No tri-state at this period */ + /* Tri-state option for output data */ +#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ + /* 1: No tri-state at this period */ +#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ + +/* COM4 */ + /* PLL frequency control */ +#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ +#define PLL_4x 0x40 /* 01: PLL 4x */ +#define PLL_6x 0x80 /* 10: PLL 6x */ +#define PLL_8x 0xc0 /* 11: PLL 8x */ + /* AEC evaluate window */ +#define AEC_FULL 0x00 /* 00: Full window */ +#define AEC_1p2 0x10 /* 01: 1/2 window */ +#define AEC_1p4 0x20 /* 10: 1/4 window */ +#define AEC_2p3 0x30 /* 11: Low 2/3 window */ +#define COM4_RESERVED 0x01 /* Reserved bit */ + +/* COM5 */ +#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ +#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ + /* Auto frame rate max rate control */ +#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ +#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ +#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ +#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ + /* Auto frame rate active point control */ +#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ +#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ +#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ +#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ + /* AEC max step control */ +#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ + /* 1 : No limit to AEC increase step */ +/* CLKRC */ + /* Input clock divider register */ +#define CLKRC_RESERVED 0x80 /* Reserved bit */ +#define CLKRC_DIV(n) ((n) - 1) + +/* COM7 */ + /* SCCB Register Reset */ +#define SCCB_RESET 0x80 /* 0 : No change */ + /* 1 : Resets all registers to default */ + /* Resolution selection */ +#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ +#define SLCT_VGA 0x00 /* 0 : VGA */ +#define SLCT_QVGA 0x40 /* 1 : QVGA */ +#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ +#define SENSOR_RAW 0x10 /* Sensor RAW */ + /* RGB output format control */ +#define FMT_MASK 0x0c /* Mask of color format */ +#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ +#define FMT_RGB565 0x04 /* 01 : RGB 565 */ +#define FMT_RGB555 0x08 /* 10 : RGB 555 */ +#define FMT_RGB444 0x0c /* 11 : RGB 444 */ + /* Output format control */ +#define OFMT_MASK 0x03 /* Mask of output format */ +#define OFMT_YUV 0x00 /* 00 : YUV */ +#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ +#define OFMT_RGB 0x02 /* 10 : RGB */ +#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ + +/* COM8 */ +#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ + /* AEC Setp size limit */ +#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ + /* 1 : Unlimited step size */ +#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ +#define AEC_BND 0x10 /* Enable AEC below banding value */ +#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ +#define AGC_ON 0x04 /* AGC Enable */ +#define AWB_ON 0x02 /* AWB Enable */ +#define AEC_ON 0x01 /* AEC Enable */ + +/* COM9 */ +#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ + /* Automatic gain ceiling - maximum AGC value */ +#define GAIN_2x 0x00 /* 000 : 2x */ +#define GAIN_4x 0x10 /* 001 : 4x */ +#define GAIN_8x 0x20 /* 010 : 8x */ +#define GAIN_16x 0x30 /* 011 : 16x */ +#define GAIN_32x 0x40 /* 100 : 32x */ +#define GAIN_64x 0x50 /* 101 : 64x */ +#define GAIN_128x 0x60 /* 110 : 128x */ +#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ +#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ + +/* COM11 */ +#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ +#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ + +/* HREF */ +#define HREF_VSTART_SHIFT 6 /* VSTART LSB */ +#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ +#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ +#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ + +/* EXHCH */ +#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ +#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ + +/* DSP_CTRL1 */ +#define FIFO_ON 0x80 /* FIFO enable/disable selection */ +#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ +#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ +#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ +#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ +#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ +#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ +#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ + +/* DSP_CTRL3 */ +#define UV_MASK 0x80 /* UV output sequence option */ +#define UV_ON 0x80 /* ON */ +#define UV_OFF 0x00 /* OFF */ +#define CBAR_MASK 0x20 /* DSP Color bar mask */ +#define CBAR_ON 0x20 /* ON */ +#define CBAR_OFF 0x00 /* OFF */ + +/* DSP_CTRL4 */ +#define DSP_OFMT_YUV 0x00 +#define DSP_OFMT_RGB 0x00 +#define DSP_OFMT_RAW8 0x02 +#define DSP_OFMT_RAW10 0x03 + +/* DSPAUTO (DSP Auto Function ON/OFF Control) */ +#define AWB_ACTRL 0x80 /* AWB auto threshold control */ +#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ +#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ +#define UV_ACTRL 0x10 /* UV adjust auto slope control */ +#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ +#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ + +#define OV772X_MAX_WIDTH VGA_WIDTH +#define OV772X_MAX_HEIGHT VGA_HEIGHT + +/* + * ID + */ +#define OV7720 0x7720 +#define OV7725 0x7721 +#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) + +/* + * PLL multipliers + */ +static struct { + unsigned int mult; + u8 com4; +} ov772x_pll[] = { + { 1, PLL_BYPASS, }, + { 4, PLL_4x, }, + { 6, PLL_6x, }, + { 8, PLL_8x, }, +}; + +/* + * struct + */ + +struct ov772x_color_format { + u32 code; + enum v4l2_colorspace colorspace; + u8 dsp3; + u8 dsp4; + u8 com3; + u8 com7; +}; + +struct ov772x_win_size { + char *name; + unsigned char com7_bit; + unsigned int sizeimage; + struct v4l2_rect rect; +}; + +struct ov772x_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct clk *clk; + struct ov772x_camera_info *info; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *rstb_gpio; + const struct ov772x_color_format *cfmt; + const struct ov772x_win_size *win; + unsigned short flag_vflip:1; + unsigned short flag_hflip:1; + /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ + unsigned short band_filter; + unsigned int fps; +}; + +/* + * supported color format list + */ +static const struct ov772x_color_format ov772x_cfmts[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = UV_ON, + .dsp4 = DSP_OFMT_YUV, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = 0x0, + .com7 = OFMT_YUV, + }, + { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = SWAP_RGB, + .com7 = FMT_RGB555 | OFMT_RGB, + }, + { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = 0x0, + .com7 = FMT_RGB555 | OFMT_RGB, + }, + { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = SWAP_RGB, + .com7 = FMT_RGB565 | OFMT_RGB, + }, + { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_YUV, + .com3 = 0x0, + .com7 = FMT_RGB565 | OFMT_RGB, + }, + { + /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, + * regardless of the COM7 value. We can thus only support 10-bit + * Bayer until someone figures it out. + */ + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .dsp3 = 0x0, + .dsp4 = DSP_OFMT_RAW10, + .com3 = 0x0, + .com7 = SENSOR_RAW | OFMT_BRAW, + }, +}; + +/* + * window size list + */ + +static const struct ov772x_win_size ov772x_win_sizes[] = { + { + .name = "VGA", + .com7_bit = SLCT_VGA, + .sizeimage = 510 * 748, + .rect = { + .left = 140, + .top = 14, + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + }, + }, { + .name = "QVGA", + .com7_bit = SLCT_QVGA, + .sizeimage = 278 * 576, + .rect = { + .left = 252, + .top = 6, + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + }, + }, +}; + +/* + * frame rate settings lists + */ +static const unsigned int ov772x_frame_intervals[] = { 5, 10, 15, 20, 30, 60 }; + +/* + * general function + */ + +static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov772x_priv, subdev); +} + +static inline int ov772x_read(struct i2c_client *client, u8 addr) +{ + return i2c_smbus_read_byte_data(client, addr); +} + +static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) +{ + return i2c_smbus_write_byte_data(client, addr, value); +} + +static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, + u8 set) +{ + s32 val = ov772x_read(client, command); + + if (val < 0) + return val; + + val &= ~mask; + val |= set & mask; + + return ov772x_write(client, command, val); +} + +static int ov772x_reset(struct i2c_client *client) +{ + int ret; + + ret = ov772x_write(client, COM7, SCCB_RESET); + if (ret < 0) + return ret; + + usleep_range(1000, 5000); + + return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); +} + +/* + * subdev ops + */ + +static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov772x_priv *priv = to_ov772x(sd); + + if (!enable) { + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); + return 0; + } + + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); + + dev_dbg(&client->dev, "format %d, win %s\n", + priv->cfmt->code, priv->win->name); + + return 0; +} + +static int ov772x_set_frame_rate(struct ov772x_priv *priv, + struct v4l2_fract *tpf, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + unsigned long fin = clk_get_rate(priv->clk); + unsigned int fps = tpf->numerator ? + tpf->denominator / tpf->numerator : + tpf->denominator; + unsigned int best_diff; + unsigned int fsize; + unsigned int pclk; + unsigned int diff; + unsigned int idx; + unsigned int i; + u8 clkrc = 0; + u8 com4 = 0; + int ret; + + /* Approximate to the closest supported frame interval. */ + best_diff = ~0L; + for (i = 0, idx = 0; i < ARRAY_SIZE(ov772x_frame_intervals); i++) { + diff = abs(fps - ov772x_frame_intervals[i]); + if (diff < best_diff) { + idx = i; + best_diff = diff; + } + } + fps = ov772x_frame_intervals[idx]; + + /* Use image size (with blankings) to calculate desired pixel clock. */ + switch (cfmt->com7 & OFMT_MASK) { + case OFMT_BRAW: + fsize = win->sizeimage; + break; + case OFMT_RGB: + case OFMT_YUV: + default: + fsize = win->sizeimage * 2; + break; + } + + pclk = fps * fsize; + + /* + * Pixel clock generation circuit is pretty simple: + * + * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk + * + * Try to approximate the desired pixel clock testing all available + * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding + * divisor with: + * + * div = PLL_mult * Fin / pclk + * + * and re-calculate the pixel clock using it: + * + * pclk = Fin * PLL_mult / CLKRC_div + * + * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock + * closer to the desired one. + * + * The desired pixel clock is calculated using a known frame size + * (blanking included) and FPS. + */ + best_diff = ~0L; + for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) { + unsigned int pll_mult = ov772x_pll[i].mult; + unsigned int pll_out = pll_mult * fin; + unsigned int t_pclk; + unsigned int div; + + if (pll_out < pclk) + continue; + + div = DIV_ROUND_CLOSEST(pll_out, pclk); + t_pclk = DIV_ROUND_CLOSEST(fin * pll_mult, div); + diff = abs(pclk - t_pclk); + if (diff < best_diff) { + best_diff = diff; + clkrc = CLKRC_DIV(div); + com4 = ov772x_pll[i].com4; + } + } + + ret = ov772x_write(client, COM4, com4 | COM4_RESERVED); + if (ret < 0) + return ret; + + ret = ov772x_write(client, CLKRC, clkrc | CLKRC_RESERVED); + if (ret < 0) + return ret; + + tpf->numerator = 1; + tpf->denominator = fps; + priv->fps = tpf->denominator; + + return 0; +} + +static int ov772x_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct ov772x_priv *priv = to_ov772x(sd); + struct v4l2_fract *tpf = &ival->interval; + + tpf->numerator = 1; + tpf->denominator = priv->fps; + + return 0; +} + +static int ov772x_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct ov772x_priv *priv = to_ov772x(sd); + struct v4l2_fract *tpf = &ival->interval; + + return ov772x_set_frame_rate(priv, tpf, priv->cfmt, priv->win); +} + +static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov772x_priv *priv = container_of(ctrl->handler, + struct ov772x_priv, hdl); + struct v4l2_subdev *sd = &priv->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + u8 val; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + val = ctrl->val ? VFLIP_IMG : 0x00; + priv->flag_vflip = ctrl->val; + if (priv->info->flags & OV772X_FLAG_VFLIP) + val ^= VFLIP_IMG; + return ov772x_mask_set(client, COM3, VFLIP_IMG, val); + case V4L2_CID_HFLIP: + val = ctrl->val ? HFLIP_IMG : 0x00; + priv->flag_hflip = ctrl->val; + if (priv->info->flags & OV772X_FLAG_HFLIP) + val ^= HFLIP_IMG; + return ov772x_mask_set(client, COM3, HFLIP_IMG, val); + case V4L2_CID_BAND_STOP_FILTER: + if (!ctrl->val) { + /* Switch the filter off, it is on now */ + ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); + if (!ret) + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, 0); + } else { + /* Switch the filter on, set AEC low limit */ + val = 256 - ctrl->val; + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, BNDF_ON_OFF); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, val); + } + if (!ret) + priv->band_filter = ctrl->val; + return ret; + } + + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov772x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + reg->size = 1; + if (reg->reg > 0xff) + return -EINVAL; + + ret = ov772x_read(client, reg->reg); + if (ret < 0) + return ret; + + reg->val = (__u64)ret; + + return 0; +} + +static int ov772x_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return ov772x_write(client, reg->reg, reg->val); +} +#endif + +static int ov772x_power_on(struct ov772x_priv *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + int ret; + + if (priv->clk) { + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + } + + if (priv->pwdn_gpio) { + gpiod_set_value(priv->pwdn_gpio, 1); + usleep_range(500, 1000); + } + + /* + * FIXME: The reset signal is connected to a shared GPIO on some + * platforms (namely the SuperH Migo-R). Until a framework becomes + * available to handle this cleanly, request the GPIO temporarily + * to avoid conflicts. + */ + priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb", + GPIOD_OUT_LOW); + if (IS_ERR(priv->rstb_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + return PTR_ERR(priv->rstb_gpio); + } + + if (priv->rstb_gpio) { + gpiod_set_value(priv->rstb_gpio, 1); + usleep_range(500, 1000); + gpiod_set_value(priv->rstb_gpio, 0); + usleep_range(500, 1000); + + gpiod_put(priv->rstb_gpio); + } + + return 0; +} + +static int ov772x_power_off(struct ov772x_priv *priv) +{ + clk_disable_unprepare(priv->clk); + + if (priv->pwdn_gpio) { + gpiod_set_value(priv->pwdn_gpio, 0); + usleep_range(500, 1000); + } + + return 0; +} + +static int ov772x_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov772x_priv *priv = to_ov772x(sd); + + return on ? ov772x_power_on(priv) : + ov772x_power_off(priv); +} + +static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) +{ + const struct ov772x_win_size *win = &ov772x_win_sizes[0]; + u32 best_diff = UINT_MAX; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { + u32 diff = abs(width - ov772x_win_sizes[i].rect.width) + + abs(height - ov772x_win_sizes[i].rect.height); + if (diff < best_diff) { + best_diff = diff; + win = &ov772x_win_sizes[i]; + } + } + + return win; +} + +static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, + const struct ov772x_color_format **cfmt, + const struct ov772x_win_size **win) +{ + unsigned int i; + + /* Select a format. */ + *cfmt = &ov772x_cfmts[0]; + + for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { + if (mf->code == ov772x_cfmts[i].code) { + *cfmt = &ov772x_cfmts[i]; + break; + } + } + + /* Select a window size. */ + *win = ov772x_select_win(mf->width, mf->height); +} + +static int ov772x_set_params(struct ov772x_priv *priv, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + struct v4l2_fract tpf; + int ret; + u8 val; + + /* Reset hardware. */ + ov772x_reset(client); + + /* Edge Ctrl. */ + if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { + /* + * Manual Edge Control Mode. + * + * Edge auto strength bit is set by default. + * Remove it when manual mode. + */ + + ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(client, + EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, + priv->info->edgectrl.threshold); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(client, + EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, + priv->info->edgectrl.strength); + if (ret < 0) + goto ov772x_set_fmt_error; + + } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { + /* + * Auto Edge Control Mode. + * + * Set upper and lower limit. + */ + ret = ov772x_mask_set(client, + EDGE_UPPER, OV772X_EDGE_UPPER_MASK, + priv->info->edgectrl.upper); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(client, + EDGE_LOWER, OV772X_EDGE_LOWER_MASK, + priv->info->edgectrl.lower); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* Format and window size. */ + ret = ov772x_write(client, HSTART, win->rect.left >> 2); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, HSIZE, win->rect.width >> 2); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, VSTART, win->rect.top >> 1); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, VSIZE, win->rect.height >> 1); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, HREF, + ((win->rect.top & 1) << HREF_VSTART_SHIFT) | + ((win->rect.left & 3) << HREF_HSTART_SHIFT) | + ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | + ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); + if (ret < 0) + goto ov772x_set_fmt_error; + ret = ov772x_write(client, EXHCH, + ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | + ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* Set DSP_CTRL3. */ + val = cfmt->dsp3; + if (val) { + ret = ov772x_mask_set(client, + DSP_CTRL3, UV_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* DSP_CTRL4: AEC reference point and DSP output format. */ + if (cfmt->dsp4) { + ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* Set COM3. */ + val = cfmt->com3; + if (priv->info->flags & OV772X_FLAG_VFLIP) + val |= VFLIP_IMG; + if (priv->info->flags & OV772X_FLAG_HFLIP) + val |= HFLIP_IMG; + if (priv->flag_vflip) + val ^= VFLIP_IMG; + if (priv->flag_hflip) + val ^= HFLIP_IMG; + + ret = ov772x_mask_set(client, + COM3, SWAP_MASK | IMG_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* COM7: Sensor resolution and output format control. */ + ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* COM4, CLKRC: Set pixel clock and framerate. */ + tpf.numerator = 1; + tpf.denominator = priv->fps; + ret = ov772x_set_frame_rate(priv, &tpf, cfmt, win); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* Set COM8. */ + if (priv->band_filter) { + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, 256 - priv->band_filter); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + return ret; + +ov772x_set_fmt_error: + + ov772x_reset(client); + + return ret; +} + +static int ov772x_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov772x_priv *priv = to_ov772x(sd); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.width = priv->win->rect.width; + sel->r.height = priv->win->rect.height; + return 0; + default: + return -EINVAL; + } +} + +static int ov772x_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct ov772x_priv *priv = to_ov772x(sd); + + if (format->pad) + return -EINVAL; + + mf->width = priv->win->rect.width; + mf->height = priv->win->rect.height; + mf->code = priv->cfmt->code; + mf->colorspace = priv->cfmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov772x_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov772x_priv *priv = to_ov772x(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + const struct ov772x_color_format *cfmt; + const struct ov772x_win_size *win; + int ret; + + if (format->pad) + return -EINVAL; + + ov772x_select_params(mf, &cfmt, &win); + + mf->code = cfmt->code; + mf->width = win->rect.width; + mf->height = win->rect.height; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = cfmt->colorspace; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + ret = ov772x_set_params(priv, cfmt, win); + if (ret < 0) + return ret; + + priv->win = win; + priv->cfmt = cfmt; + + return 0; +} + +static int ov772x_video_probe(struct ov772x_priv *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + u8 pid, ver; + const char *devname; + int ret; + + ret = ov772x_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* Check and show product ID and manufacturer ID. */ + pid = ov772x_read(client, PID); + ver = ov772x_read(client, VER); + + switch (VERSION(pid, ver)) { + case OV7720: + devname = "ov7720"; + break; + case OV7725: + devname = "ov7725"; + break; + default: + dev_err(&client->dev, + "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, + "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, + pid, + ver, + ov772x_read(client, MIDH), + ov772x_read(client, MIDL)); + ret = v4l2_ctrl_handler_setup(&priv->hdl); + +done: + ov772x_s_power(&priv->subdev, 0); + + return ret; +} + +static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { + .s_ctrl = ov772x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov772x_g_register, + .s_register = ov772x_s_register, +#endif + .s_power = ov772x_s_power, +}; + +static int ov772x_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals)) + return -EINVAL; + + if (fie->width != VGA_WIDTH && fie->width != QVGA_WIDTH) + return -EINVAL; + if (fie->height != VGA_HEIGHT && fie->height != QVGA_HEIGHT) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = ov772x_frame_intervals[fie->index]; + + return 0; +} + +static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) + return -EINVAL; + + code->code = ov772x_cfmts[code->index].code; + + return 0; +} + +static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { + .s_stream = ov772x_s_stream, + .s_frame_interval = ov772x_s_frame_interval, + .g_frame_interval = ov772x_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { + .enum_frame_interval = ov772x_enum_frame_interval, + .enum_mbus_code = ov772x_enum_mbus_code, + .get_selection = ov772x_get_selection, + .get_fmt = ov772x_get_fmt, + .set_fmt = ov772x_set_fmt, +}; + +static const struct v4l2_subdev_ops ov772x_subdev_ops = { + .core = &ov772x_subdev_core_ops, + .video = &ov772x_subdev_video_ops, + .pad = &ov772x_subdev_pad_ops, +}; + +/* + * i2c_driver function + */ + +static int ov772x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov772x_priv *priv; + struct i2c_adapter *adapter = client->adapter; + int ret; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "Missing ov772x platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_PROTOCOL_MANGLING)) { + dev_err(&adapter->dev, + "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n"); + return -EIO; + } + client->flags |= I2C_CLIENT_SCCB; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = client->dev.platform_data; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 3); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) + return priv->hdl.error; + + priv->clk = clk_get(&client->dev, "xclk"); + if (IS_ERR(priv->clk)) { + dev_err(&client->dev, "Unable to get xclk clock\n"); + ret = PTR_ERR(priv->clk); + goto error_ctrl_free; + } + + priv->pwdn_gpio = gpiod_get_optional(&client->dev, "pwdn", + GPIOD_OUT_LOW); + if (IS_ERR(priv->pwdn_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"pwdn\""); + ret = PTR_ERR(priv->pwdn_gpio); + goto error_clk_put; + } + + ret = ov772x_video_probe(priv); + if (ret < 0) + goto error_gpio_put; + + priv->cfmt = &ov772x_cfmts[0]; + priv->win = &ov772x_win_sizes[0]; + priv->fps = 15; + + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret) + goto error_gpio_put; + + return 0; + +error_gpio_put: + if (priv->pwdn_gpio) + gpiod_put(priv->pwdn_gpio); +error_clk_put: + clk_put(priv->clk); +error_ctrl_free: + v4l2_ctrl_handler_free(&priv->hdl); + + return ret; +} + +static int ov772x_remove(struct i2c_client *client) +{ + struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); + + clk_put(priv->clk); + if (priv->pwdn_gpio) + gpiod_put(priv->pwdn_gpio); + v4l2_async_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); + + return 0; +} + +static const struct i2c_device_id ov772x_id[] = { + { "ov772x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov772x_id); + +static struct i2c_driver ov772x_i2c_driver = { + .driver = { + .name = "ov772x", + }, + .probe = ov772x_probe, + .remove = ov772x_remove, + .id_table = ov772x_id, +}; + +module_i2c_driver(ov772x_i2c_driver); + +MODULE_DESCRIPTION("V4L2 driver for OV772x image sensor"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index fc9dbbcae56e..01f578785e79 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -279,7 +279,7 @@ static int ov7740_get_register(struct v4l2_subdev *sd, reg->val = val; reg->size = 1; - return 0; + return ret; } static int ov7740_set_register(struct v4l2_subdev *sd, @@ -624,17 +624,11 @@ err_unlock: return ret; } -static int ov7740_get_parm(struct v4l2_subdev *sd, - struct v4l2_streamparm *parms) +static int ov7740_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + struct v4l2_fract *tpf = &ival->interval; - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; tpf->numerator = 1; tpf->denominator = 60; @@ -642,18 +636,11 @@ static int ov7740_get_parm(struct v4l2_subdev *sd, return 0; } -static int ov7740_set_parm(struct v4l2_subdev *sd, - struct v4l2_streamparm *parms) +static int ov7740_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; + struct v4l2_fract *tpf = &ival->interval; - cp->capability = V4L2_CAP_TIMEPERFRAME; tpf->numerator = 1; tpf->denominator = 60; @@ -663,8 +650,8 @@ static int ov7740_set_parm(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { .s_stream = ov7740_set_stream, - .s_parm = ov7740_set_parm, - .g_parm = ov7740_get_parm, + .s_frame_interval = ov7740_s_frame_interval, + .g_frame_interval = ov7740_g_frame_interval, }; static const struct reg_sequence ov7740_format_yuyv[] = { diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index e519f278d5f9..5bea31cd41aa 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -11,8 +11,10 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/media.h> @@ -249,9 +251,10 @@ struct ov965x { struct v4l2_subdev sd; struct media_pad pad; enum v4l2_mbus_type bus_type; - int gpios[NUM_GPIOS]; + struct gpio_desc *gpios[NUM_GPIOS]; /* External master clock frequency */ unsigned long mclk_frequency; + struct clk *clk; /* Protects the struct fields below */ struct mutex lock; @@ -513,24 +516,27 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x) return 0; } -static void ov965x_gpio_set(int gpio, int val) -{ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, val); -} - -static void __ov965x_set_power(struct ov965x *ov965x, int on) +static int __ov965x_set_power(struct ov965x *ov965x, int on) { if (on) { - ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0); - ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0); + int ret = clk_prepare_enable(ov965x->clk); + + if (ret) + return ret; + + gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0); msleep(25); } else { - ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1); - ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1); + gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1); + + clk_disable_unprepare(ov965x->clk); } ov965x->streaming = 0; + + return 0; } static int ov965x_s_power(struct v4l2_subdev *sd, int on) @@ -543,8 +549,8 @@ static int ov965x_s_power(struct v4l2_subdev *sd, int on) mutex_lock(&ov965x->lock); if (ov965x->power == !on) { - __ov965x_set_power(ov965x, on); - if (on) { + ret = __ov965x_set_power(ov965x, on); + if (!ret && on) { ret = ov965x_write_array(client, ov965x_init_regs); ov965x->apply_frame_fmt = 1; @@ -1130,8 +1136,8 @@ static int __ov965x_set_frame_interval(struct ov965x *ov965x, if (fi->interval.denominator == 0) return -EINVAL; - req_int = (u64)(fi->interval.numerator * 10000) / - fi->interval.denominator; + req_int = (u64)fi->interval.numerator * 10000; + do_div(req_int, fi->interval.denominator); for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) { const struct ov965x_interval *iv = &ov965x_intervals[i]; @@ -1410,16 +1416,17 @@ static const struct v4l2_subdev_ops ov965x_subdev_ops = { /* * Reset and power down GPIOs configuration */ -static int ov965x_configure_gpios(struct ov965x *ov965x, - const struct ov9650_platform_data *pdata) +static int ov965x_configure_gpios_pdata(struct ov965x *ov965x, + const struct ov9650_platform_data *pdata) { int ret, i; + int gpios[NUM_GPIOS]; - ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn; - ov965x->gpios[GPIO_RST] = pdata->gpio_reset; + gpios[GPIO_PWDN] = pdata->gpio_pwdn; + gpios[GPIO_RST] = pdata->gpio_reset; for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) { - int gpio = ov965x->gpios[i]; + int gpio = gpios[i]; if (!gpio_is_valid(gpio)) continue; @@ -1429,9 +1436,30 @@ static int ov965x_configure_gpios(struct ov965x *ov965x, return ret; v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio); - gpio_set_value(gpio, 1); + gpio_set_value_cansleep(gpio, 1); gpio_export(gpio, 0); - ov965x->gpios[i] = gpio; + ov965x->gpios[i] = gpio_to_desc(gpio); + } + + return 0; +} + +static int ov965x_configure_gpios(struct ov965x *ov965x) +{ + struct device *dev = &ov965x->client->dev; + + ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ov965x->gpios[GPIO_PWDN])) { + dev_info(dev, "can't get %s GPIO\n", "powerdown"); + return PTR_ERR(ov965x->gpios[GPIO_PWDN]); + } + + ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov965x->gpios[GPIO_RST])) { + dev_info(dev, "can't get %s GPIO\n", "reset"); + return PTR_ERR(ov965x->gpios[GPIO_RST]); } return 0; @@ -1445,7 +1473,10 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) int ret; mutex_lock(&ov965x->lock); - __ov965x_set_power(ov965x, 1); + ret = __ov965x_set_power(ov965x, 1); + if (ret) + goto out; + msleep(25); /* Check sensor revision */ @@ -1465,6 +1496,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) ret = -ENODEV; } } +out: mutex_unlock(&ov965x->lock); return ret; @@ -1478,23 +1510,39 @@ static int ov965x_probe(struct i2c_client *client, struct ov965x *ov965x; int ret; - if (!pdata) { - dev_err(&client->dev, "platform data not specified\n"); - return -EINVAL; - } - - if (pdata->mclk_frequency == 0) { - dev_err(&client->dev, "MCLK frequency not specified\n"); - return -EINVAL; - } - ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); if (!ov965x) return -ENOMEM; - mutex_init(&ov965x->lock); ov965x->client = client; - ov965x->mclk_frequency = pdata->mclk_frequency; + + if (pdata) { + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + ov965x->mclk_frequency = pdata->mclk_frequency; + + ret = ov965x_configure_gpios_pdata(ov965x, pdata); + if (ret < 0) + return ret; + } else if (dev_fwnode(&client->dev)) { + ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL); + if (IS_ERR(ov965x->clk)) + return PTR_ERR(ov965x->clk); + ov965x->mclk_frequency = clk_get_rate(ov965x->clk); + + ret = ov965x_configure_gpios(ov965x); + if (ret < 0) + return ret; + } else { + dev_err(&client->dev, + "Neither platform data nor device property specified\n"); + + return -EINVAL; + } + + mutex_init(&ov965x->lock); sd = &ov965x->sd; v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); @@ -1504,10 +1552,6 @@ static int ov965x_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - ret = ov965x_configure_gpios(ov965x, pdata); - if (ret < 0) - goto err_mutex; - ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad); @@ -1563,9 +1607,19 @@ static const struct i2c_device_id ov965x_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov965x_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov965x_of_match[] = { + { .compatible = "ovti,ov9650", }, + { .compatible = "ovti,ov9652", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov965x_of_match); +#endif + static struct i2c_driver ov965x_i2c_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ov965x_of_match), }, .probe = ov965x_probe, .remove = ov965x_remove, diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index cdc4f2392ef9..ce196b60f917 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -248,17 +248,17 @@ static int s5c73m3_check_status(struct s5c73m3 *state, unsigned int value) { unsigned long start = jiffies; unsigned long end = start + msecs_to_jiffies(2000); - int ret = 0; + int ret; u16 status; int count = 0; - while (time_is_after_jiffies(end)) { + do { ret = s5c73m3_read(state, REG_STATUS, &status); if (ret < 0 || status == value) break; usleep_range(500, 1000); ++count; - } + } while (time_is_after_jiffies(end)); if (count > 0) v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index c3089bd34df2..33d2987f9555 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -411,9 +411,9 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) break; /* --- poll() for /dev/radio --- */ case SAA6588_CMD_POLL: - a->result = 0; + a->poll_mask = 0; if (s->data_available_for_read) - a->result |= EPOLLIN | EPOLLRDNORM; + a->poll_mask |= EPOLLIN | EPOLLRDNORM; poll_wait(a->instance, &s->read_queue, a->event_list); break; diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 72b369895b37..7c2aabc8a3f6 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -1,11 +1,5 @@ comment "soc_camera sensor drivers" -config SOC_CAMERA_IMX074 - tristate "imx074 support" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony - config SOC_CAMERA_MT9M001 tristate "mt9m001 support" depends on SOC_CAMERA && I2C @@ -23,12 +17,6 @@ config SOC_CAMERA_MT9M111 This is the legacy configuration which shouldn't be used anymore, while VIDEO_MT9M111 should be used instead. -config SOC_CAMERA_MT9T031 - tristate "mt9t031 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T031 cameras from Micron. - config SOC_CAMERA_MT9T112 tristate "mt9t112 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index faa2df8901d2..8c7770f62997 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c deleted file mode 100644 index 77f1e0243d6e..000000000000 --- a/drivers/media/i2c/soc_camera/imx074.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Driver for IMX074 CMOS Image Sensor from Sony - * - * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * Partially inspired by the IMX074 driver from the Android / MSM tree - * - * 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. - */ - -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/v4l2-mediabus.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/soc_camera.h> -#include <media/v4l2-async.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-subdev.h> - -/* IMX074 registers */ - -#define MODE_SELECT 0x0100 -#define IMAGE_ORIENTATION 0x0101 -#define GROUPED_PARAMETER_HOLD 0x0104 - -/* Integration Time */ -#define COARSE_INTEGRATION_TIME_HI 0x0202 -#define COARSE_INTEGRATION_TIME_LO 0x0203 -/* Gain */ -#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 -#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 - -/* PLL registers */ -#define PRE_PLL_CLK_DIV 0x0305 -#define PLL_MULTIPLIER 0x0307 -#define PLSTATIM 0x302b -#define VNDMY_ABLMGSHLMT 0x300a -#define Y_OPBADDR_START_DI 0x3014 -/* mode setting */ -#define FRAME_LENGTH_LINES_HI 0x0340 -#define FRAME_LENGTH_LINES_LO 0x0341 -#define LINE_LENGTH_PCK_HI 0x0342 -#define LINE_LENGTH_PCK_LO 0x0343 -#define YADDR_START 0x0347 -#define YADDR_END 0x034b -#define X_OUTPUT_SIZE_MSB 0x034c -#define X_OUTPUT_SIZE_LSB 0x034d -#define Y_OUTPUT_SIZE_MSB 0x034e -#define Y_OUTPUT_SIZE_LSB 0x034f -#define X_EVEN_INC 0x0381 -#define X_ODD_INC 0x0383 -#define Y_EVEN_INC 0x0385 -#define Y_ODD_INC 0x0387 - -#define HMODEADD 0x3001 -#define VMODEADD 0x3016 -#define VAPPLINE_START 0x3069 -#define VAPPLINE_END 0x306b -#define SHUTTER 0x3086 -#define HADDAVE 0x30e8 -#define LANESEL 0x3301 - -/* IMX074 supported geometry */ -#define IMX074_WIDTH 1052 -#define IMX074_HEIGHT 780 - -/* IMX074 has only one fixed colorspace per pixelcode */ -struct imx074_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct imx074 { - struct v4l2_subdev subdev; - const struct imx074_datafmt *fmt; - struct v4l2_clk *clk; -}; - -static const struct imx074_datafmt imx074_colour_fmts[] = { - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static struct imx074 *to_imx074(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct imx074, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct imx074_datafmt *imx074_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) - if (imx074_colour_fmts[i].code == code) - return imx074_colour_fmts + i; - - return NULL; -} - -static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) -{ - struct i2c_adapter *adap = client->adapter; - struct i2c_msg msg; - unsigned char tx[3]; - int ret; - - msg.addr = client->addr; - msg.buf = tx; - msg.len = 3; - msg.flags = 0; - - tx[0] = addr >> 8; - tx[1] = addr & 0xff; - tx[2] = data; - - ret = i2c_transfer(adap, &msg, 1); - - mdelay(2); - - return ret == 1 ? 0 : -EIO; -} - -static int reg_read(struct i2c_client *client, const u16 addr) -{ - u8 buf[2] = {addr >> 8, addr & 0xff}; - int ret; - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 2, - .buf = buf, - }, - }; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) { - dev_warn(&client->dev, "Reading register %x from %x failed\n", - addr, client->addr); - return ret; - } - - return buf[0] & 0xff; /* no sign-extension */ -} - -static int imx074_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - if (format->pad) - return -EINVAL; - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - if (!fmt) { - /* MIPI CSI could have changed the format, double-check */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = imx074_colour_fmts[0].code; - mf->colorspace = imx074_colour_fmts[0].colorspace; - } - - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - - return 0; -} - -static int imx074_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - const struct imx074_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int imx074_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX074_WIDTH; - sel->r.height = IMX074_HEIGHT; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP: - return 0; - default: - return -EINVAL; - } -} - -static int imx074_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || - (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) - return -EINVAL; - - code->code = imx074_colour_fmts[code->index].code; - return 0; -} - -static int imx074_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* MODE_SELECT: stream or standby */ - return reg_write(client, MODE_SELECT, !!enable); -} - -static int imx074_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int imx074_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { - .s_stream = imx074_s_stream, - .g_mbus_config = imx074_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { - .s_power = imx074_s_power, -}; - -static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { - .enum_mbus_code = imx074_enum_mbus_code, - .get_selection = imx074_get_selection, - .get_fmt = imx074_get_fmt, - .set_fmt = imx074_set_fmt, -}; - -static const struct v4l2_subdev_ops imx074_subdev_ops = { - .core = &imx074_subdev_core_ops, - .video = &imx074_subdev_video_ops, - .pad = &imx074_subdev_pad_ops, -}; - -static int imx074_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u16 id; - - ret = imx074_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, 0); - if (ret < 0) - goto done; - - id = ret << 8; - - ret = reg_read(client, 1); - if (ret < 0) - goto done; - - id |= ret; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x74) { - ret = -ENODEV; - goto done; - } - - /* PLL Setting EXTCLK=24MHz, 22.5times */ - reg_write(client, PLL_MULTIPLIER, 0x2D); - reg_write(client, PRE_PLL_CLK_DIV, 0x02); - reg_write(client, PLSTATIM, 0x4B); - - /* 2-lane mode */ - reg_write(client, 0x3024, 0x00); - - reg_write(client, IMAGE_ORIENTATION, 0x00); - - /* select RAW mode: - * 0x08+0x08 = top 8 bits - * 0x0a+0x08 = compressed 8-bits - * 0x0a+0x0a = 10 bits - */ - reg_write(client, 0x0112, 0x08); - reg_write(client, 0x0113, 0x08); - - /* Base setting for High frame mode */ - reg_write(client, VNDMY_ABLMGSHLMT, 0x80); - reg_write(client, Y_OPBADDR_START_DI, 0x08); - reg_write(client, 0x3015, 0x37); - reg_write(client, 0x301C, 0x01); - reg_write(client, 0x302C, 0x05); - reg_write(client, 0x3031, 0x26); - reg_write(client, 0x3041, 0x60); - reg_write(client, 0x3051, 0x24); - reg_write(client, 0x3053, 0x34); - reg_write(client, 0x3057, 0xC0); - reg_write(client, 0x305C, 0x09); - reg_write(client, 0x305D, 0x07); - reg_write(client, 0x3060, 0x30); - reg_write(client, 0x3065, 0x00); - reg_write(client, 0x30AA, 0x08); - reg_write(client, 0x30AB, 0x1C); - reg_write(client, 0x30B0, 0x32); - reg_write(client, 0x30B2, 0x83); - reg_write(client, 0x30D3, 0x04); - reg_write(client, 0x3106, 0x78); - reg_write(client, 0x310C, 0x82); - reg_write(client, 0x3304, 0x05); - reg_write(client, 0x3305, 0x04); - reg_write(client, 0x3306, 0x11); - reg_write(client, 0x3307, 0x02); - reg_write(client, 0x3308, 0x0C); - reg_write(client, 0x3309, 0x06); - reg_write(client, 0x330A, 0x08); - reg_write(client, 0x330B, 0x04); - reg_write(client, 0x330C, 0x08); - reg_write(client, 0x330D, 0x06); - reg_write(client, 0x330E, 0x01); - reg_write(client, 0x3381, 0x00); - - /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ - /* 1608 = 1560 + 48 (black lines) */ - reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); - reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); - reg_write(client, YADDR_START, 0x00); - reg_write(client, YADDR_END, 0x2F); - /* 0x838 == 2104 */ - reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); - reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); - /* 0x618 == 1560 */ - reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); - reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); - reg_write(client, X_EVEN_INC, 0x01); - reg_write(client, X_ODD_INC, 0x03); - reg_write(client, Y_EVEN_INC, 0x01); - reg_write(client, Y_ODD_INC, 0x03); - reg_write(client, HMODEADD, 0x00); - reg_write(client, VMODEADD, 0x16); - reg_write(client, VAPPLINE_START, 0x24); - reg_write(client, VAPPLINE_END, 0x53); - reg_write(client, SHUTTER, 0x00); - reg_write(client, HADDAVE, 0x80); - - reg_write(client, LANESEL, 0x00); - - reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ - - ret = 0; - -done: - imx074_s_power(subdev, 0); - return ret; -} - -static int imx074_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct imx074 *priv; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "IMX074: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - - priv->fmt = &imx074_colour_fmts[0]; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); - return -EPROBE_DEFER; - } - - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto epwrinit; - - ret = imx074_video_probe(client); - if (ret < 0) - goto eprobe; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; - -epwrinit: -eprobe: - v4l2_clk_put(priv->clk); - return ret; -} - -static int imx074_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id imx074_id[] = { - { "imx074", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, imx074_id); - -static struct i2c_driver imx074_i2c_driver = { - .driver = { - .name = "imx074", - }, - .probe = imx074_probe, - .remove = imx074_remove, - .id_table = imx074_id, -}; - -module_i2c_driver(imx074_i2c_driver); - -MODULE_DESCRIPTION("Sony IMX074 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c deleted file mode 100644 index 4802d30e47de..000000000000 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Driver for MT9T031 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de> - * - * 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. - */ - -#include <linux/device.h> -#include <linux/i2c.h> -#include <linux/log2.h> -#include <linux/pm.h> -#include <linux/slab.h> -#include <linux/v4l2-mediabus.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/soc_camera.h> -#include <media/v4l2-clk.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-ctrls.h> - -/* - * ATTENTION: this driver still cannot be used outside of the soc-camera - * framework because of its PM implementation, using the video_device node. - * If hardware becomes available for testing, alternative PM approaches shall - * be considered and tested. - */ - -/* - * mt9t031 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9t031 selected register addresses */ -#define MT9T031_CHIP_VERSION 0x00 -#define MT9T031_ROW_START 0x01 -#define MT9T031_COLUMN_START 0x02 -#define MT9T031_WINDOW_HEIGHT 0x03 -#define MT9T031_WINDOW_WIDTH 0x04 -#define MT9T031_HORIZONTAL_BLANKING 0x05 -#define MT9T031_VERTICAL_BLANKING 0x06 -#define MT9T031_OUTPUT_CONTROL 0x07 -#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9T031_SHUTTER_WIDTH 0x09 -#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9T031_FRAME_RESTART 0x0b -#define MT9T031_SHUTTER_DELAY 0x0c -#define MT9T031_RESET 0x0d -#define MT9T031_READ_MODE_1 0x1e -#define MT9T031_READ_MODE_2 0x20 -#define MT9T031_READ_MODE_3 0x21 -#define MT9T031_ROW_ADDRESS_MODE 0x22 -#define MT9T031_COLUMN_ADDRESS_MODE 0x23 -#define MT9T031_GLOBAL_GAIN 0x35 -#define MT9T031_CHIP_ENABLE 0xF8 - -#define MT9T031_MAX_HEIGHT 1536 -#define MT9T031_MAX_WIDTH 2048 -#define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 18 -#define MT9T031_HORIZONTAL_BLANK 142 -#define MT9T031_VERTICAL_BLANK 25 -#define MT9T031_COLUMN_SKIP 32 -#define MT9T031_ROW_SKIP 20 - -struct mt9t031 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - u16 xskip; - u16 yskip; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9t031 *to_mt9t031(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int set_shutter(struct i2c_client *client, const u32 data) -{ - int ret; - - ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); - - if (ret >= 0) - ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); - - return ret; -} - -static int get_shutter(struct i2c_client *client, u32 *data) -{ - int ret; - - ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); - *data = ret << 16; - - if (ret >= 0) - ret = reg_read(client, MT9T031_SHUTTER_WIDTH); - *data |= ret & 0xffff; - - return ret < 0 ? ret : 0; -} - -static int mt9t031_idle(struct i2c_client *client) -{ - int ret; - - /* Disable chip output, synchronous option update */ - ret = reg_write(client, MT9T031_RESET, 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_RESET, 0); - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - return ret >= 0 ? 0 : -EIO; -} - -static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (enable) - /* Switch to master "normal" mode */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); - else - /* Stop sensor readout */ - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* target must be _even_ */ -static u16 mt9t031_skip(s32 *source, s32 target, s32 max) -{ - unsigned int skip; - - if (*source < target + target / 2) { - *source = target; - return 1; - } - - skip = min(max, *source + target / 2) / target; - if (skip > 8) - skip = 8; - *source = target * skip; - - return skip; -} - -/* rect is the sensor rectangle, the caller guarantees parameter validity */ -static int mt9t031_set_params(struct i2c_client *client, - struct v4l2_rect *rect, u16 xskip, u16 yskip) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - u16 xbin, ybin; - const u16 hblank = MT9T031_HORIZONTAL_BLANK, - vblank = MT9T031_VERTICAL_BLANK; - - xbin = min(xskip, (u16)3); - ybin = min(yskip, (u16)3); - - /* - * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. - * There is always a valid suitably aligned value. The worst case is - * xbin = 3, width = 2048. Then we will start at 36, the last read out - * pixel will be 2083, which is < 2085 - first black pixel. - * - * MT9T031 datasheet imposes window left border alignment, depending on - * the selected xskip. Failing to conform to this requirement produces - * dark horizontal stripes in the image. However, even obeying to this - * requirement doesn't eliminate the stripes in all configurations. They - * appear "locally reproducibly," but can differ between tests under - * different lighting conditions. - */ - switch (xbin) { - case 1: - rect->left &= ~1; - break; - case 2: - rect->left &= ~3; - break; - case 3: - rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? - (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); - } - - rect->top &= ~1; - - dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", - xskip, yskip, rect->width, rect->height, rect->left, rect->top); - - /* Disable register update, reconfigure atomically */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); - if (ret < 0) - return ret; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) - ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - - if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { - /* Binning, skipping */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - } - dev_dbg(&client->dev, "new physical left %u, top %u\n", - rect->left, rect->top); - - /* - * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() - */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, rect->left); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, rect->top); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { - mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; - - ret = set_shutter(client, mt9t031->total_h); - } - - /* Re-enable register update, commit all changes */ - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); - - if (ret >= 0) { - mt9t031->rect = *rect; - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } - - return ret < 0 ? ret : 0; -} - -static int mt9t031_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - struct v4l2_rect rect = sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - - return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); -} - -static int mt9t031_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.left = MT9T031_COLUMN_SKIP; - sel->r.top = MT9T031_ROW_SKIP; - sel->r.width = MT9T031_MAX_WIDTH; - sel->r.height = MT9T031_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9t031->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t031_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9t031->rect.width / mt9t031->xskip; - mf->height = mt9t031->rect.height / mt9t031->yskip; - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - u16 xskip, yskip; - struct v4l2_rect rect = mt9t031->rect; - - if (format->pad) - return -EINVAL; - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Width and height are within limits. - * S_FMT: use binning and skipping for scaling - */ - xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); - yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - /* mt9t031_set_params() doesn't change width and height */ - return mt9t031_set_params(client, &rect, xskip, yskip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9t031_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9t031->exposure->minimum; - max = mt9t031->exposure->maximum; - mt9t031->exposure->val = - (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) - / shutter_max + min; - break; - } - return 0; -} - -static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - struct v4l2_subdev *sd = &mt9t031->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9t031->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 1015 + range / 2) / range + 9; - - if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ - data = gain; - else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ - data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - - dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", - reg_read(client, MT9T031_GLOBAL_GAIN), data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - u32 old; - - get_shutter(client, &old); - dev_dbg(&client->dev, "Set shutter from %u to %u\n", - old, shutter); - if (set_shutter(client, shutter) < 0) - return -EIO; - } else { - const u16 vblank = MT9T031_VERTICAL_BLANK; - mt9t031->total_h = mt9t031->rect.height + - mt9t031->y_skip_top + vblank; - - if (set_shutter(client, mt9t031->total_h) < 0) - return -EIO; - } - return 0; - default: - return -EINVAL; - } - return 0; -} - -/* - * Power Management: - * This function does nothing for now but must be present for pm to work - */ -static int mt9t031_runtime_suspend(struct device *dev) -{ - return 0; -} - -/* - * Power Management: - * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged - * they are however changed at reset if the platform hook is present - * thus we rewrite them with the values stored by the driver - */ -static int mt9t031_runtime_resume(struct device *dev) -{ - struct video_device *vdev = to_video_device(dev); - struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - int ret; - u16 xbin, ybin; - - xbin = min(mt9t031->xskip, (u16)3); - ybin = min(mt9t031->yskip, (u16)3); - - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (mt9t031->xskip - 1)); - if (ret < 0) - return ret; - - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (mt9t031->yskip - 1)); - if (ret < 0) - return ret; - - return 0; -} - -static const struct dev_pm_ops mt9t031_dev_pm_ops = { - .runtime_suspend = mt9t031_runtime_suspend, - .runtime_resume = mt9t031_runtime_resume, -}; - -static const struct device_type mt9t031_dev_type = { - .name = "MT9T031", - .pm = &mt9t031_dev_pm_ops, -}; - -static int mt9t031_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); - if (ret < 0) - return ret; - if (vdev) - /* Not needed during probing, when vdev isn't available yet */ - vdev->dev.type = &mt9t031_dev_type; - } else { - if (vdev) - vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); - } - - return 0; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9t031_video_probe(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - s32 data; - int ret; - - ret = mt9t031_s_power(&mt9t031->subdev, 1); - if (ret < 0) - return ret; - - ret = mt9t031_idle(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* Read out the chip version register */ - data = reg_read(client, MT9T031_CHIP_VERSION); - - switch (data) { - case 0x1621: - break; - default: - dev_err(&client->dev, - "No MT9T031 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - - ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); - -done: - mt9t031_s_power(&mt9t031->subdev, 0); - - return ret; -} - -static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - *lines = mt9t031->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { - .g_volatile_ctrl = mt9t031_g_volatile_ctrl, - .s_ctrl = mt9t031_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .s_power = mt9t031_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t031_g_register, - .s_register = mt9t031_s_register, -#endif -}; - -static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_FALLING) - return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); -} - -static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { - .s_stream = mt9t031_s_stream, - .g_mbus_config = mt9t031_g_mbus_config, - .s_mbus_config = mt9t031_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { - .g_skip_top_lines = mt9t031_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { - .enum_mbus_code = mt9t031_enum_mbus_code, - .get_selection = mt9t031_get_selection, - .set_selection = mt9t031_set_selection, - .get_fmt = mt9t031_get_fmt, - .set_fmt = mt9t031_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9t031_subdev_ops = { - .core = &mt9t031_subdev_core_ops, - .video = &mt9t031_subdev_video_ops, - .sensor = &mt9t031_subdev_sensor_ops, - .pad = &mt9t031_subdev_pad_ops, -}; - -static int mt9t031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t031 *mt9t031; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); - if (!mt9t031) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); - v4l2_ctrl_handler_init(&mt9t031->hdl, 5); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, - &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) - return mt9t031->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - mt9t031->y_skip_top = 0; - mt9t031->rect.left = MT9T031_COLUMN_SKIP; - mt9t031->rect.top = MT9T031_ROW_SKIP; - mt9t031->rect.width = MT9T031_MAX_WIDTH; - mt9t031->rect.height = MT9T031_MAX_HEIGHT; - - mt9t031->xskip = 1; - mt9t031->yskip = 1; - - mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9t031->clk)) { - ret = PTR_ERR(mt9t031->clk); - goto eclkget; - } - - ret = mt9t031_video_probe(client); - if (ret) { - v4l2_clk_put(mt9t031->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9t031->hdl); - } - - return ret; -} - -static int mt9t031_remove(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - - v4l2_clk_put(mt9t031->clk); - v4l2_device_unregister_subdev(&mt9t031->subdev); - v4l2_ctrl_handler_free(&mt9t031->hdl); - - return 0; -} - -static const struct i2c_device_id mt9t031_id[] = { - { "mt9t031", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t031_id); - -static struct i2c_driver mt9t031_i2c_driver = { - .driver = { - .name = "mt9t031", - }, - .probe = mt9t031_probe, - .remove = mt9t031_remove, - .id_table = mt9t031_id, -}; - -module_i2c_driver(mt9t031_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 297d22ebcb18..b53c36dfa469 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -85,7 +85,7 @@ struct mt9t112_format { struct mt9t112_priv { struct v4l2_subdev subdev; - struct mt9t112_camera_info *info; + struct mt9t112_platform_data *info; struct i2c_client *client; struct v4l2_rect frame; struct v4l2_clk *clk; diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index 0bf031b7e4fa..2a4882cddc51 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -511,13 +511,16 @@ static int sr030pc30_get_fmt(struct v4l2_subdev *sd, static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { - int i = ARRAY_SIZE(sr030pc30_formats); + int i; sr030pc30_try_frame_size(mf); - while (i--) + for (i = 0; i < ARRAY_SIZE(sr030pc30_formats); i++) { if (mf->code == sr030pc30_formats[i].code) break; + } + if (i == ARRAY_SIZE(sr030pc30_formats)) + i = 0; mf->code = sr030pc30_formats[i].code; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 2b8181469b93..393bbbbbaad7 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tc358743 - Toshiba HDMI to CSI-2 bridge * * Copyright 2015 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. - * */ /* diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h index 227b46471793..2495878dc358 100644 --- a/drivers/media/i2c/tc358743_regs.h +++ b/drivers/media/i2c/tc358743_regs.h @@ -1,22 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks * * Copyright 2015 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. - * */ /* diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c new file mode 100644 index 000000000000..3021913c28fa --- /dev/null +++ b/drivers/media/i2c/tda1997x.c @@ -0,0 +1,2820 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Gateworks Corporation + */ +#include <linux/delay.h> +#include <linux/hdmi.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/v4l2-dv-timings.h> +#include <linux/videodev2.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/i2c/tda1997x.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <dt-bindings/media/tda1997x.h> + +#include "tda1997x_regs.h" + +#define TDA1997X_MBUS_CODES 5 + +/* debug level */ +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +/* Audio formats */ +static const char * const audtype_names[] = { + "PCM", /* PCM Samples */ + "HBR", /* High Bit Rate Audio */ + "OBA", /* One-Bit Audio */ + "DST" /* Direct Stream Transfer */ +}; + +/* Audio output port formats */ +enum audfmt_types { + AUDFMT_TYPE_DISABLED = 0, + AUDFMT_TYPE_I2S, + AUDFMT_TYPE_SPDIF, +}; +static const char * const audfmt_names[] = { + "Disabled", + "I2S", + "SPDIF", +}; + +/* Video input formats */ +static const char * const hdmi_colorspace_names[] = { + "RGB", "YUV422", "YUV444", "YUV420", "", "", "", "", +}; +static const char * const hdmi_colorimetry_names[] = { + "", "ITU601", "ITU709", "Extended", +}; +static const char * const v4l2_quantization_names[] = { + "Default", + "Full Range (0-255)", + "Limited Range (16-235)", +}; + +/* Video output port formats */ +static const char * const vidfmt_names[] = { + "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */ + "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */ + "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */ + "Invalid", +}; + +/* + * Colorspace conversion matrices + */ +struct color_matrix_coefs { + const char *name; + /* Input offsets */ + s16 offint1; + s16 offint2; + s16 offint3; + /* Coeficients */ + s16 p11coef; + s16 p12coef; + s16 p13coef; + s16 p21coef; + s16 p22coef; + s16 p23coef; + s16 p31coef; + s16 p32coef; + s16 p33coef; + /* Output offsets */ + s16 offout1; + s16 offout2; + s16 offout3; +}; + +enum { + ITU709_RGBFULL, + ITU601_RGBFULL, + RGBLIMITED_RGBFULL, + RGBLIMITED_ITU601, + RGBLIMITED_ITU709, + RGBFULL_ITU601, + RGBFULL_ITU709, +}; + +/* NB: 4096 is 1.0 using fixed point numbers */ +static const struct color_matrix_coefs conv_matrix[] = { + { + "YUV709 -> RGB full", + -256, -2048, -2048, + 4769, -2183, -873, + 4769, 7343, 0, + 4769, 0, 8652, + 0, 0, 0, + }, + { + "YUV601 -> RGB full", + -256, -2048, -2048, + 4769, -3330, -1602, + 4769, 6538, 0, + 4769, 0, 8264, + 256, 256, 256, + }, + { + "RGB limited -> RGB full", + -256, -256, -256, + 0, 4769, 0, + 0, 0, 4769, + 4769, 0, 0, + 0, 0, 0, + }, + { + "RGB limited -> ITU601", + -256, -256, -256, + 2404, 1225, 467, + -1754, 2095, -341, + -1388, -707, 2095, + 256, 2048, 2048, + }, + { + "RGB limited -> ITU709", + -256, -256, -256, + 2918, 867, 295, + -1894, 2087, -190, + -1607, -477, 2087, + 256, 2048, 2048, + }, + { + "RGB full -> ITU601", + 0, 0, 0, + 2065, 1052, 401, + -1506, 1799, -293, + -1192, -607, 1799, + 256, 2048, 2048, + }, + { + "RGB full -> ITU709", + 0, 0, 0, + 2506, 745, 253, + -1627, 1792, -163, + -1380, -410, 1792, + 256, 2048, 2048, + }, +}; + +static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with GCC < 4.4.6 */ + .reserved = { 0 }, + + V4L2_INIT_BT_TIMINGS( + 640, 1920, /* min/max width */ + 350, 1200, /* min/max height */ + 13000000, 165000000, /* min/max pixelclock */ + /* standards */ + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + /* capabilities */ + V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM + ) +}; + +/* regulator supplies */ +static const char * const tda1997x_supply_name[] = { + "DOVDD", /* Digital I/O supply */ + "DVDD", /* Digital Core supply */ + "AVDD", /* Analog supply */ +}; + +#define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name) + +enum tda1997x_type { + TDA19971, + TDA19973, +}; + +enum tda1997x_hdmi_pads { + TDA1997X_PAD_SOURCE, + TDA1997X_NUM_PADS, +}; + +struct tda1997x_chip_info { + enum tda1997x_type type; + const char *name; +}; + +struct tda1997x_state { + const struct tda1997x_chip_info *info; + struct tda1997x_platform_data pdata; + struct i2c_client *client; + struct i2c_client *client_cec; + struct v4l2_subdev sd; + struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES]; + struct media_pad pads[TDA1997X_NUM_PADS]; + struct mutex lock; + struct mutex page_lock; + char page; + + /* detected info from chip */ + int chip_revision; + char port_30bit; + char output_2p5; + char tmdsb_clk; + char tmdsb_soc; + + /* status info */ + char hdmi_status; + char mptrw_in_progress; + char activity_status; + char input_detect[2]; + + /* video */ + struct hdmi_avi_infoframe avi_infoframe; + struct v4l2_hdmi_colorimetry colorimetry; + u32 rgb_quantization_range; + struct v4l2_dv_timings timings; + int fps; + const struct color_matrix_coefs *conv; + u32 mbus_codes[TDA1997X_MBUS_CODES]; /* available modes */ + u32 mbus_code; /* current mode */ + u8 vid_fmt; + + /* controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *rgb_quantization_range_ctrl; + + /* audio */ + u8 audio_ch_alloc; + int audio_samplerate; + int audio_channels; + int audio_samplesize; + int audio_type; + struct mutex audio_lock; + struct snd_pcm_substream *audio_stream; + + /* EDID */ + struct { + u8 edid[256]; + u32 present; + unsigned int blocks; + } edid; + struct delayed_work delayed_work_enable_hpd; +}; + +static const struct v4l2_event tda1997x_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + +static const struct tda1997x_chip_info tda1997x_chip_info[] = { + [TDA19971] = { + .type = TDA19971, + .name = "tda19971", + }, + [TDA19973] = { + .type = TDA19973, + .name = "tda19973", + }, +}; + +static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda1997x_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd; +} + +static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg) +{ + struct tda1997x_state *state = to_state(sd); + int val; + + val = i2c_smbus_read_byte_data(state->client_cec, reg); + if (val < 0) { + v4l_err(state->client, "read reg error: reg=%2x\n", reg); + val = -1; + } + + return val; +} + +static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct tda1997x_state *state = to_state(sd); + int ret = 0; + + ret = i2c_smbus_write_byte_data(state->client_cec, reg, val); + if (ret < 0) { + v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", + reg, val); + ret = -1; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C transfer + */ + +static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page) +{ + struct tda1997x_state *state = to_state(sd); + int ret; + + if (state->page != page) { + ret = i2c_smbus_write_byte_data(state->client, + REG_CURPAGE_00H, page); + if (ret < 0) { + v4l_err(state->client, + "write reg error:reg=%2x,val=%2x\n", + REG_CURPAGE_00H, page); + return ret; + } + state->page = page; + } + return 0; +} + +static inline int io_read(struct v4l2_subdev *sd, u16 reg) +{ + struct tda1997x_state *state = to_state(sd); + int val; + + mutex_lock(&state->page_lock); + if (tda1997x_setpage(sd, reg >> 8)) { + val = -1; + goto out; + } + + val = i2c_smbus_read_byte_data(state->client, reg&0xff); + if (val < 0) { + v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff); + val = -1; + goto out; + } + +out: + mutex_unlock(&state->page_lock); + return val; +} + +static inline long io_read16(struct v4l2_subdev *sd, u16 reg) +{ + int val; + long lval = 0; + + val = io_read(sd, reg); + if (val < 0) + return val; + lval |= (val << 8); + val = io_read(sd, reg + 1); + if (val < 0) + return val; + lval |= val; + + return lval; +} + +static inline long io_read24(struct v4l2_subdev *sd, u16 reg) +{ + int val; + long lval = 0; + + val = io_read(sd, reg); + if (val < 0) + return val; + lval |= (val << 16); + val = io_read(sd, reg + 1); + if (val < 0) + return val; + lval |= (val << 8); + val = io_read(sd, reg + 2); + if (val < 0) + return val; + lval |= val; + + return lval; +} + +static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data) +{ + int i; + int sz = 0; + int val; + + for (i = 0; i < len; i++) { + val = io_read(sd, reg + i); + if (val < 0) + break; + data[i] = val; + sz++; + } + + return sz; +} + +static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val) +{ + struct tda1997x_state *state = to_state(sd); + s32 ret = 0; + + mutex_lock(&state->page_lock); + if (tda1997x_setpage(sd, reg >> 8)) { + ret = -1; + goto out; + } + + ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val); + if (ret < 0) { + v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n", + reg&0xff, val); + ret = -1; + goto out; + } + +out: + mutex_unlock(&state->page_lock); + return ret; +} + +static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + int ret; + + ret = io_write(sd, reg, (val >> 8) & 0xff); + if (ret < 0) + return ret; + ret = io_write(sd, reg + 1, val & 0xff); + if (ret < 0) + return ret; + return 0; +} + +static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val) +{ + int ret; + + ret = io_write(sd, reg, (val >> 16) & 0xff); + if (ret < 0) + return ret; + ret = io_write(sd, reg + 1, (val >> 8) & 0xff); + if (ret < 0) + return ret; + ret = io_write(sd, reg + 2, val & 0xff); + if (ret < 0) + return ret; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Hotplug + */ + +enum hpd_mode { + HPD_LOW_BP, /* HPD low and pulse of at least 100ms */ + HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */ + HPD_HIGH_BP, /* HIGH */ + HPD_HIGH_OTHER, + HPD_PULSE, /* HPD low pulse */ +}; + +/* manual HPD (Hot Plug Detect) control */ +static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode) +{ + u8 hpd_auto, hpd_pwr, hpd_man; + + hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL); + hpd_pwr = io_read(sd, REG_HPD_POWER); + hpd_man = io_read(sd, REG_HPD_MAN_CTRL); + + /* mask out unused bits */ + hpd_man &= (HPD_MAN_CTRL_HPD_PULSE | + HPD_MAN_CTRL_5VEN | + HPD_MAN_CTRL_HPD_B | + HPD_MAN_CTRL_HPD_A); + + switch (mode) { + /* HPD low and pulse of at least 100ms */ + case HPD_LOW_BP: + /* hpd_bp=0 */ + hpd_pwr &= ~HPD_POWER_BP_MASK; + /* disable HPD_A and HPD_B */ + hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); + io_write(sd, REG_HPD_POWER, hpd_pwr); + io_write(sd, REG_HPD_MAN_CTRL, hpd_man); + break; + /* HPD high */ + case HPD_HIGH_BP: + /* hpd_bp=1 */ + hpd_pwr &= ~HPD_POWER_BP_MASK; + hpd_pwr |= 1 << HPD_POWER_BP_SHIFT; + io_write(sd, REG_HPD_POWER, hpd_pwr); + break; + /* HPD low and pulse of at least 100ms */ + case HPD_LOW_OTHER: + /* disable HPD_A and HPD_B */ + hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); + /* hp_other=0 */ + hpd_auto &= ~HPD_AUTO_HP_OTHER; + io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); + io_write(sd, REG_HPD_MAN_CTRL, hpd_man); + break; + /* HPD high */ + case HPD_HIGH_OTHER: + hpd_auto |= HPD_AUTO_HP_OTHER; + io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto); + break; + /* HPD low pulse */ + case HPD_PULSE: + /* disable HPD_A and HPD_B */ + hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B); + io_write(sd, REG_HPD_MAN_CTRL, hpd_man); + break; + } + + return 0; +} + +static void tda1997x_delayed_work_enable_hpd(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tda1997x_state *state = container_of(dwork, + struct tda1997x_state, + delayed_work_enable_hpd); + struct v4l2_subdev *sd = &state->sd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Set HPD high */ + tda1997x_manual_hpd(sd, HPD_HIGH_OTHER); + tda1997x_manual_hpd(sd, HPD_HIGH_BP); + + state->edid.present = 1; +} + +static void tda1997x_disable_edid(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + cancel_delayed_work_sync(&state->delayed_work_enable_hpd); + + /* Set HPD low */ + tda1997x_manual_hpd(sd, HPD_LOW_BP); +} + +static void tda1997x_enable_edid(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* Enable hotplug after 100ms */ + schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10); +} + +/* ----------------------------------------------------------------------------- + * Signal Control + */ + +/* + * configure vid_fmt based on mbus_code + */ +static int +tda1997x_setup_format(struct tda1997x_state *state, u32 code) +{ + v4l_dbg(1, debug, state->client, "%s code=0x%x\n", __func__, code); + switch (code) { + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV8_1X24: + state->vid_fmt = OF_FMT_444; + break; + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY8_1X16: + state->vid_fmt = OF_FMT_422_SMPT; + break; + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_UYVY8_2X8: + state->vid_fmt = OF_FMT_422_CCIR; + break; + default: + v4l_err(state->client, "incompatible format (0x%x)\n", code); + return -EINVAL; + } + v4l_dbg(1, debug, state->client, "%s code=0x%x fmt=%s\n", __func__, + code, vidfmt_names[state->vid_fmt]); + state->mbus_code = code; + + return 0; +} + +/* + * The color conversion matrix will convert between the colorimetry of the + * HDMI input to the desired output format RGB|YUV. RGB output is to be + * full-range and YUV is to be limited range. + * + * RGB full-range uses values from 0 to 255 which is recommended on a monitor + * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is + * typically recommended on a TV. + */ +static void +tda1997x_configure_csc(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + struct hdmi_avi_infoframe *avi = &state->avi_infoframe; + struct v4l2_hdmi_colorimetry *c = &state->colorimetry; + /* Blanking code values depend on output colorspace (RGB or YUV) */ + struct blanking_codes { + s16 code_gy; + s16 code_bu; + s16 code_rv; + }; + static const struct blanking_codes rgb_blanking = { 64, 64, 64 }; + static const struct blanking_codes yuv_blanking = { 64, 512, 512 }; + const struct blanking_codes *blanking_codes = NULL; + u8 reg; + + v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n", + hdmi_colorspace_names[avi->colorspace], + v4l2_quantization_names[c->quantization], + vidfmt_names[state->vid_fmt]); + state->conv = NULL; + switch (state->vid_fmt) { + /* RGB output */ + case OF_FMT_444: + blanking_codes = &rgb_blanking; + if (c->colorspace == V4L2_COLORSPACE_SRGB) { + if (c->quantization == V4L2_QUANTIZATION_LIM_RANGE) + state->conv = &conv_matrix[RGBLIMITED_RGBFULL]; + } else { + if (c->colorspace == V4L2_COLORSPACE_REC709) + state->conv = &conv_matrix[ITU709_RGBFULL]; + else if (c->colorspace == V4L2_COLORSPACE_SMPTE170M) + state->conv = &conv_matrix[ITU601_RGBFULL]; + } + break; + + /* YUV output */ + case OF_FMT_422_SMPT: /* semi-planar */ + case OF_FMT_422_CCIR: /* CCIR656 */ + blanking_codes = &yuv_blanking; + if ((c->colorspace == V4L2_COLORSPACE_SRGB) && + (c->quantization == V4L2_QUANTIZATION_FULL_RANGE)) { + if (state->timings.bt.height <= 576) + state->conv = &conv_matrix[RGBFULL_ITU601]; + else + state->conv = &conv_matrix[RGBFULL_ITU709]; + } else if ((c->colorspace == V4L2_COLORSPACE_SRGB) && + (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)) { + if (state->timings.bt.height <= 576) + state->conv = &conv_matrix[RGBLIMITED_ITU601]; + else + state->conv = &conv_matrix[RGBLIMITED_ITU709]; + } + break; + } + + if (state->conv) { + v4l_dbg(1, debug, state->client, "%s\n", + state->conv->name); + /* enable matrix conversion */ + reg = io_read(sd, REG_VDP_CTRL); + reg &= ~VDP_CTRL_MATRIX_BP; + io_write(sd, REG_VDP_CTRL, reg); + /* offset inputs */ + io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1); + io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2); + io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3); + /* coefficients */ + io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef); + io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef); + io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef); + io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef); + io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef); + io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef); + io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef); + io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef); + io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef); + /* offset outputs */ + io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1); + io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2); + io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3); + } else { + /* disable matrix conversion */ + reg = io_read(sd, REG_VDP_CTRL); + reg |= VDP_CTRL_MATRIX_BP; + io_write(sd, REG_VDP_CTRL, reg); + } + + /* SetBlankingCodes */ + if (blanking_codes) { + io_write16(sd, REG_BLK_GY, blanking_codes->code_gy); + io_write16(sd, REG_BLK_BU, blanking_codes->code_bu); + io_write16(sd, REG_BLK_RV, blanking_codes->code_rv); + } +} + +/* Configure frame detection window and VHREF timing generator */ +static void +tda1997x_configure_vhref(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + const struct v4l2_bt_timings *bt = &state->timings.bt; + int width, lines; + u16 href_start, href_end; + u16 vref_f1_start, vref_f2_start; + u8 vref_f1_width, vref_f2_width; + u8 field_polarity; + u16 fieldref_f1_start, fieldref_f2_start; + u8 reg; + + href_start = bt->hbackporch + bt->hsync + 1; + href_end = href_start + bt->width; + vref_f1_start = bt->height + bt->vbackporch + bt->vsync + + bt->il_vbackporch + bt->il_vsync + + bt->il_vfrontporch; + vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch; + vref_f2_start = 0; + vref_f2_width = 0; + fieldref_f1_start = 0; + fieldref_f2_start = 0; + if (bt->interlaced) { + vref_f2_start = (bt->height / 2) + + (bt->il_vbackporch + bt->il_vsync - 1); + vref_f2_width = bt->il_vbackporch + bt->il_vsync + + bt->il_vfrontporch; + fieldref_f2_start = vref_f2_start + bt->il_vfrontporch + + fieldref_f1_start; + } + field_polarity = 0; + + width = V4L2_DV_BT_FRAME_WIDTH(bt); + lines = V4L2_DV_BT_FRAME_HEIGHT(bt); + + /* + * Configure Frame Detection Window: + * horiz area where the VHREF module consider a VSYNC a new frame + */ + io_write16(sd, REG_FDW_S, 0x2ef); /* start position */ + io_write16(sd, REG_FDW_E, 0x141); /* end position */ + + /* Set Pixel And Line Counters */ + if (state->chip_revision == 0) + io_write16(sd, REG_PXCNT_PR, 4); + else + io_write16(sd, REG_PXCNT_PR, 1); + io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF); + io_write16(sd, REG_LCNT_PR, 1); + io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF); + + /* + * Configure the VHRef timing generator responsible for rebuilding all + * horiz and vert synch and ref signals from its input allowing auto + * detection algorithms and forcing predefined modes (480i & 576i) + */ + reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT; + io_write(sd, REG_VHREF_CTRL, reg); + + /* + * Configure the VHRef timing values. In case the VHREF generator has + * been configured in manual mode, this will allow to manually set all + * horiz and vert ref values (non-active pixel areas) of the generator + * and allows setting the frame reference params. + */ + /* horizontal reference start/end */ + io_write16(sd, REG_HREF_S, href_start & MASK_VHREF); + io_write16(sd, REG_HREF_E, href_end & MASK_VHREF); + /* vertical reference f1 start/end */ + io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF); + io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width); + /* vertical reference f2 start/end */ + io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF); + io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width); + + /* F1/F2 FREF, field polarity */ + reg = fieldref_f1_start & MASK_VHREF; + reg |= field_polarity << 8; + io_write16(sd, REG_FREF_F1_S, reg); + reg = fieldref_f2_start & MASK_VHREF; + io_write16(sd, REG_FREF_F2_S, reg); +} + +/* Configure Video Output port signals */ +static int +tda1997x_configure_vidout(struct tda1997x_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + struct tda1997x_platform_data *pdata = &state->pdata; + u8 prefilter; + u8 reg; + + /* Configure pixel clock generator: delay, polarity, rate */ + reg = (state->vid_fmt == OF_FMT_422_CCIR) ? + PCLK_SEL_X2 : PCLK_SEL_X1; + reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT; + reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT; + io_write(sd, REG_PCLK, reg); + + /* Configure pre-filter */ + prefilter = 0; /* filters off */ + /* YUV422 mode requires conversion */ + if ((state->vid_fmt == OF_FMT_422_SMPT) || + (state->vid_fmt == OF_FMT_422_CCIR)) { + /* 2/7 taps for Rv and Bu */ + prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT | + FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT; + } + io_write(sd, REG_FILTERS_CTRL, prefilter); + + /* Configure video port */ + reg = state->vid_fmt & OF_FMT_MASK; + if (state->vid_fmt == OF_FMT_422_CCIR) + reg |= (OF_BLK | OF_TRC); + reg |= OF_VP_ENABLE; + io_write(sd, REG_OF, reg); + + /* Configure formatter and conversions */ + reg = io_read(sd, REG_VDP_CTRL); + /* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */ + if (!prefilter) + reg |= VDP_CTRL_PREFILTER_BP; + else + reg &= ~VDP_CTRL_PREFILTER_BP; + /* formatter is needed for YUV422 and for trc/blc codes */ + if (state->vid_fmt == OF_FMT_444) + reg |= VDP_CTRL_FORMATTER_BP; + /* formatter and compdel needed for timing/blanking codes */ + else + reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP); + /* activate compdel for small sync delays */ + if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) + reg &= ~VDP_CTRL_COMPDEL_BP; + io_write(sd, REG_VDP_CTRL, reg); + + /* Configure DE output signal: delay, polarity, and source */ + reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT | + pdata->vidout_inv_de << DE_FREF_INV_SHIFT | + pdata->vidout_sel_de << DE_FREF_SEL_SHIFT; + io_write(sd, REG_DE_FREF, reg); + + /* Configure HS/HREF output signal: delay, polarity, and source */ + if (state->vid_fmt != OF_FMT_422_CCIR) { + reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT | + pdata->vidout_inv_hs << HS_HREF_INV_SHIFT | + pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT; + } else + reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT; + io_write(sd, REG_HS_HREF, reg); + + /* Configure VS/VREF output signal: delay, polarity, and source */ + if (state->vid_fmt != OF_FMT_422_CCIR) { + reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT | + pdata->vidout_inv_vs << VS_VREF_INV_SHIFT | + pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT; + } else + reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT; + io_write(sd, REG_VS_VREF, reg); + + return 0; +} + +/* Configure Audio output port signals */ +static int +tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) +{ + struct tda1997x_state *state = to_state(sd); + struct tda1997x_platform_data *pdata = &state->pdata; + bool sp_used_by_fifo = 1; + u8 reg; + + if (!pdata->audout_format) + return 0; + + /* channel assignment (CEA-861-D Table 20) */ + io_write(sd, REG_AUDIO_PATH, channel_assignment); + + /* Audio output configuration */ + reg = 0; + switch (pdata->audout_format) { + case AUDFMT_TYPE_I2S: + reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT; + break; + case AUDFMT_TYPE_SPDIF: + reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT; + break; + } + switch (state->audio_type) { + case AUDCFG_TYPE_PCM: + reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT; + break; + case AUDCFG_TYPE_OBA: + reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT; + break; + case AUDCFG_TYPE_DST: + reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT; + sp_used_by_fifo = 0; + break; + case AUDCFG_TYPE_HBR: + reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT; + if (pdata->audout_layout == 1) { + /* demuxed via AP0:AP3 */ + reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT; + if (pdata->audout_format == AUDFMT_TYPE_SPDIF) + sp_used_by_fifo = 0; + } else { + /* straight via AP0 */ + reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT; + } + break; + } + if (pdata->audout_width == 32) + reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT; + else + reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT; + + /* automatic hardware mute */ + if (pdata->audio_auto_mute) + reg |= AUDCFG_AUTO_MUTE_EN; + /* clock polarity */ + if (pdata->audout_invert_clk) + reg |= AUDCFG_CLK_INVERT; + io_write(sd, REG_AUDCFG, reg); + + /* audio layout */ + reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0; + if (!pdata->audout_layoutauto) + reg |= AUDIO_LAYOUT_MANUAL; + if (sp_used_by_fifo) + reg |= AUDIO_LAYOUT_SP_FLAG; + io_write(sd, REG_AUDIO_LAYOUT, reg); + + /* FIFO Latency value */ + io_write(sd, REG_FIFO_LATENCY_VAL, 0x80); + + /* Audio output port config */ + if (sp_used_by_fifo) { + reg = AUDIO_OUT_ENABLE_AP0; + if (channel_assignment >= 0x01) + reg |= AUDIO_OUT_ENABLE_AP1; + if (channel_assignment >= 0x04) + reg |= AUDIO_OUT_ENABLE_AP2; + if (channel_assignment >= 0x0c) + reg |= AUDIO_OUT_ENABLE_AP3; + /* specific cases where AP1 is not used */ + if ((channel_assignment == 0x04) + || (channel_assignment == 0x08) + || (channel_assignment == 0x0c) + || (channel_assignment == 0x10) + || (channel_assignment == 0x14) + || (channel_assignment == 0x18) + || (channel_assignment == 0x1c)) + reg &= ~AUDIO_OUT_ENABLE_AP1; + /* specific cases where AP2 is not used */ + if ((channel_assignment >= 0x14) + && (channel_assignment <= 0x17)) + reg &= ~AUDIO_OUT_ENABLE_AP2; + } else { + reg = AUDIO_OUT_ENABLE_AP3 | + AUDIO_OUT_ENABLE_AP2 | + AUDIO_OUT_ENABLE_AP1 | + AUDIO_OUT_ENABLE_AP0; + } + if (pdata->audout_format == AUDFMT_TYPE_I2S) + reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS); + io_write(sd, REG_AUDIO_OUT_ENABLE, reg); + + /* reset test mode to normal audio freq auto selection */ + io_write(sd, REG_TEST_MODE, 0x00); + + return 0; +} + +/* Soft Reset of specific hdmi info */ +static int +tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus) +{ + u8 reg; + + /* reset infoframe engine packets */ + reg = io_read(sd, REG_HDMI_INFO_RST); + io_write(sd, REG_HDMI_INFO_RST, info_rst); + + /* if infoframe engine has been reset clear INT_FLG_MODE */ + if (reg & RESET_IF) { + reg = io_read(sd, REG_INT_FLG_CLR_MODE); + io_write(sd, REG_INT_FLG_CLR_MODE, reg); + } + + /* Disable REFTIM to restart start-up-sequencer (SUS) */ + reg = io_read(sd, REG_RATE_CTRL); + reg &= ~RATE_REFTIM_ENABLE; + if (!reset_sus) + reg |= RATE_REFTIM_ENABLE; + reg = io_write(sd, REG_RATE_CTRL, reg); + + return 0; +} + +static void +tda1997x_power_mode(struct tda1997x_state *state, bool enable) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg; + + if (enable) { + /* Automatic control of TMDS */ + io_write(sd, REG_PON_OVR_EN, PON_DIS); + /* Enable current bias unit */ + io_write(sd, REG_CFG1, PON_EN); + /* Enable deep color PLL */ + io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS); + /* Output buffers active */ + reg = io_read(sd, REG_OF); + reg &= ~OF_VP_ENABLE; + io_write(sd, REG_OF, reg); + } else { + /* Power down EDID mode sequence */ + /* Output buffers in HiZ */ + reg = io_read(sd, REG_OF); + reg |= OF_VP_ENABLE; + io_write(sd, REG_OF, reg); + /* Disable deep color PLL */ + io_write(sd, REG_DEEP_PLL7_BYP, PON_EN); + /* Disable current bias unit */ + io_write(sd, REG_CFG1, PON_DIS); + /* Manual control of TMDS */ + io_write(sd, REG_PON_OVR_EN, PON_EN); + } +} + +static bool +tda1997x_detect_tx_5v(struct v4l2_subdev *sd) +{ + u8 reg = io_read(sd, REG_DETECT_5V); + + return ((reg & DETECT_5V_SEL) ? 1 : 0); +} + +static bool +tda1997x_detect_tx_hpd(struct v4l2_subdev *sd) +{ + u8 reg = io_read(sd, REG_DETECT_5V); + + return ((reg & DETECT_HPD) ? 1 : 0); +} + +static int +tda1997x_detect_std(struct tda1997x_state *state, + struct v4l2_dv_timings *timings) +{ + struct v4l2_subdev *sd = &state->sd; + u32 vper; + u16 hper; + u16 hsper; + int i; + + /* + * Read the FMT registers + * REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles + * REG_H_PER: Period of a line in MCLK(27MHz) cycles + * REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles + */ + vper = io_read24(sd, REG_V_PER) & MASK_VPER; + hper = io_read16(sd, REG_H_PER) & MASK_HPER; + hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; + v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper); + if (!vper || !hper || !hsper) + return -ENOLINK; + + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + const struct v4l2_bt_timings *bt; + u32 lines, width, _hper, _hsper; + u32 vmin, vmax, hmin, hmax, hsmin, hsmax; + bool vmatch, hmatch, hsmatch; + + bt = &v4l2_dv_timings_presets[i].bt; + width = V4L2_DV_BT_FRAME_WIDTH(bt); + lines = V4L2_DV_BT_FRAME_HEIGHT(bt); + _hper = (u32)bt->pixelclock / width; + if (bt->interlaced) + lines /= 2; + /* vper +/- 0.7% */ + vmin = ((27000000 / 1000) * 993) / _hper * lines; + vmax = ((27000000 / 1000) * 1007) / _hper * lines; + /* hper +/- 1.0% */ + hmin = ((27000000 / 100) * 99) / _hper; + hmax = ((27000000 / 100) * 101) / _hper; + /* hsper +/- 2 (take care to avoid 32bit overflow) */ + _hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000); + hsmin = _hsper - 2; + hsmax = _hsper + 2; + + /* vmatch matches the framerate */ + vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0; + /* hmatch matches the width */ + hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0; + /* hsmatch matches the hswidth */ + hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0; + if (hmatch && vmatch && hsmatch) { + v4l2_print_dv_timings(sd->name, "Detected format: ", + &v4l2_dv_timings_presets[i], + false); + if (timings) + *timings = v4l2_dv_timings_presets[i]; + return 0; + } + } + + v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n", + vper, hper, hsper); + return -ERANGE; +} + +/* some sort of errata workaround for chip revision 0 (N1) */ +static void tda1997x_reset_n1(struct tda1997x_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg; + + /* clear HDMI mode flag in BCAPS */ + io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK); + io_write(sd, REG_PON_OVR_EN, PON_EN); + io_write(sd, REG_PON_CBIAS, PON_EN); + io_write(sd, REG_PON_PLL, PON_EN); + + reg = io_read(sd, REG_MODE_REC_CFG1); + reg &= ~0x06; + reg |= 0x02; + io_write(sd, REG_MODE_REC_CFG1, reg); + io_write(sd, REG_CLK_CFG, CLK_CFG_DIS); + io_write(sd, REG_PON_OVR_EN, PON_DIS); + reg = io_read(sd, REG_MODE_REC_CFG1); + reg &= ~0x06; + io_write(sd, REG_MODE_REC_CFG1, reg); +} + +/* + * Activity detection must only be notified when stable_clk_x AND active_x + * bits are set to 1. If only stable_clk_x bit is set to 1 but not + * active_x, it means that the TMDS clock is not in the defined range + * and activity detection must not be notified. + */ +static u8 +tda1997x_read_activity_status_regs(struct v4l2_subdev *sd) +{ + u8 reg, status = 0; + + /* Read CLK_A_STATUS register */ + reg = io_read(sd, REG_CLK_A_STATUS); + /* ignore if not active */ + if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) + reg &= ~MASK_CLK_STABLE; + status |= ((reg & MASK_CLK_STABLE) >> 2); + + /* Read CLK_B_STATUS register */ + reg = io_read(sd, REG_CLK_B_STATUS); + /* ignore if not active */ + if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE)) + reg &= ~MASK_CLK_STABLE; + status |= ((reg & MASK_CLK_STABLE) >> 1); + + /* Read the SUS_STATUS register */ + reg = io_read(sd, REG_SUS_STATUS); + + /* If state = 5 => TMDS is locked */ + if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) + status |= MASK_SUS_STATE; + else + status &= ~MASK_SUS_STATE; + + return status; +} + +static void +set_rgb_quantization_range(struct tda1997x_state *state) +{ + struct v4l2_hdmi_colorimetry *c = &state->colorimetry; + + state->colorimetry = v4l2_hdmi_rx_colorimetry(&state->avi_infoframe, + NULL, + state->timings.bt.height); + /* If ycbcr_enc is V4L2_YCBCR_ENC_DEFAULT, we receive RGB */ + if (c->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) { + switch (state->rgb_quantization_range) { + case V4L2_DV_RGB_RANGE_LIMITED: + c->quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + case V4L2_DV_RGB_RANGE_FULL: + c->quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + } + } + v4l_dbg(1, debug, state->client, + "colorspace=%d/%d colorimetry=%d range=%s content=%d\n", + state->avi_infoframe.colorspace, c->colorspace, + state->avi_infoframe.colorimetry, + v4l2_quantization_names[c->quantization], + state->avi_infoframe.content_type); +} + +/* parse an infoframe and do some sanity checks on it */ +static unsigned int +tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) +{ + struct v4l2_subdev *sd = &state->sd; + union hdmi_infoframe frame; + u8 buffer[40]; + u8 reg; + int len, err; + + /* read data */ + len = io_readn(sd, addr, sizeof(buffer), buffer); + err = hdmi_infoframe_unpack(&frame, buffer); + if (err) { + v4l_err(state->client, + "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", + len, addr, buffer[0]); + return err; + } + hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); + switch (frame.any.type) { + /* Audio InfoFrame: see HDMI spec 8.2.2 */ + case HDMI_INFOFRAME_TYPE_AUDIO: + /* sample rate */ + switch (frame.audio.sample_frequency) { + case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: + state->audio_samplerate = 32000; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: + state->audio_samplerate = 44100; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: + state->audio_samplerate = 48000; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: + state->audio_samplerate = 88200; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: + state->audio_samplerate = 96000; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: + state->audio_samplerate = 176400; + break; + case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: + state->audio_samplerate = 192000; + break; + default: + case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: + break; + } + + /* sample size */ + switch (frame.audio.sample_size) { + case HDMI_AUDIO_SAMPLE_SIZE_16: + state->audio_samplesize = 16; + break; + case HDMI_AUDIO_SAMPLE_SIZE_20: + state->audio_samplesize = 20; + break; + case HDMI_AUDIO_SAMPLE_SIZE_24: + state->audio_samplesize = 24; + break; + case HDMI_AUDIO_SAMPLE_SIZE_STREAM: + default: + break; + } + + /* Channel Count */ + state->audio_channels = frame.audio.channels; + if (frame.audio.channel_allocation && + frame.audio.channel_allocation != state->audio_ch_alloc) { + /* use the channel assignment from the infoframe */ + state->audio_ch_alloc = frame.audio.channel_allocation; + tda1997x_configure_audout(sd, state->audio_ch_alloc); + /* reset the audio FIFO */ + tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); + } + break; + + /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ + case HDMI_INFOFRAME_TYPE_AVI: + state->avi_infoframe = frame.avi; + set_rgb_quantization_range(state); + + /* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */ + reg = io_read(sd, REG_PIX_REPEAT); + reg &= ~PIX_REPEAT_MASK_UP_SEL; + if (frame.avi.colorspace == HDMI_COLORSPACE_YUV422) + reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT); + io_write(sd, REG_PIX_REPEAT, reg); + + /* ConfigurePixelRepeater: repeat n-times each pixel */ + reg = io_read(sd, REG_PIX_REPEAT); + reg &= ~PIX_REPEAT_MASK_REP; + reg |= frame.avi.pixel_repeat; + io_write(sd, REG_PIX_REPEAT, reg); + + /* configure the receiver with the new colorspace */ + tda1997x_configure_csc(sd); + break; + default: + break; + } + return 0; +} + +static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg, source; + + source = io_read(sd, REG_INT_FLG_CLR_SUS); + io_write(sd, REG_INT_FLG_CLR_SUS, source); + + if (source & MASK_MPT) { + /* reset MTP in use flag if set */ + if (state->mptrw_in_progress) + state->mptrw_in_progress = 0; + } + + if (source & MASK_SUS_END) { + /* reset audio FIFO */ + reg = io_read(sd, REG_HDMI_INFO_RST); + reg |= MASK_SR_FIFO_FIFO_CTRL; + io_write(sd, REG_HDMI_INFO_RST, reg); + reg &= ~MASK_SR_FIFO_FIFO_CTRL; + io_write(sd, REG_HDMI_INFO_RST, reg); + + /* reset HDMI flags */ + state->hdmi_status = 0; + } + + /* filter FMT interrupt based on SUS state */ + reg = io_read(sd, REG_SUS_STATUS); + if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) + || (source & MASK_MPT)) { + source &= ~MASK_FMT; + } + + if (source & (MASK_FMT | MASK_SUS_END)) { + reg = io_read(sd, REG_SUS_STATUS); + if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) { + v4l_err(state->client, "BAD SUS STATUS\n"); + return; + } + if (debug) + tda1997x_detect_std(state, NULL); + /* notify user of change in resolution */ + v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt); + } +} + +static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 source; + + source = io_read(sd, REG_INT_FLG_CLR_DDC); + io_write(sd, REG_INT_FLG_CLR_DDC, source); + if (source & MASK_EDID_MTP) { + /* reset MTP in use flag if set */ + if (state->mptrw_in_progress) + state->mptrw_in_progress = 0; + } + + /* Detection of +5V */ + if (source & MASK_DET_5V) { + v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, + tda1997x_detect_tx_5v(sd)); + } +} + +static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg, source; + + u8 irq_status; + + source = io_read(sd, REG_INT_FLG_CLR_RATE); + io_write(sd, REG_INT_FLG_CLR_RATE, source); + + /* read status regs */ + irq_status = tda1997x_read_activity_status_regs(sd); + + /* + * read clock status reg until INT_FLG_CLR_RATE is still 0 + * after the read to make sure its the last one + */ + reg = source; + while (reg != 0) { + irq_status = tda1997x_read_activity_status_regs(sd); + reg = io_read(sd, REG_INT_FLG_CLR_RATE); + io_write(sd, REG_INT_FLG_CLR_RATE, reg); + source |= reg; + } + + /* we only pay attention to stability change events */ + if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { + int input = (source & MASK_RATE_A_ST)?0:1; + u8 mask = 1<<input; + + /* state change */ + if ((irq_status & mask) != (state->activity_status & mask)) { + /* activity lost */ + if ((irq_status & mask) == 0) { + v4l_info(state->client, + "HDMI-%c: Digital Activity Lost\n", + input+'A'); + + /* bypass up/down sampler and pixel repeater */ + reg = io_read(sd, REG_PIX_REPEAT); + reg &= ~PIX_REPEAT_MASK_UP_SEL; + reg &= ~PIX_REPEAT_MASK_REP; + io_write(sd, REG_PIX_REPEAT, reg); + + if (state->chip_revision == 0) + tda1997x_reset_n1(state); + + state->input_detect[input] = 0; + v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt); + } + + /* activity detected */ + else { + v4l_info(state->client, + "HDMI-%c: Digital Activity Detected\n", + input+'A'); + state->input_detect[input] = 1; + } + + /* hold onto current state */ + state->activity_status = (irq_status & mask); + } + } +} + +static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 source; + + source = io_read(sd, REG_INT_FLG_CLR_INFO); + io_write(sd, REG_INT_FLG_CLR_INFO, source); + + /* Audio infoframe */ + if (source & MASK_AUD_IF) { + tda1997x_parse_infoframe(state, AUD_IF); + source &= ~MASK_AUD_IF; + } + + /* Source Product Descriptor infoframe change */ + if (source & MASK_SPD_IF) { + tda1997x_parse_infoframe(state, SPD_IF); + source &= ~MASK_SPD_IF; + } + + /* Auxiliary Video Information infoframe */ + if (source & MASK_AVI_IF) { + tda1997x_parse_infoframe(state, AVI_IF); + source &= ~MASK_AVI_IF; + } +} + +static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg, source; + + source = io_read(sd, REG_INT_FLG_CLR_AUDIO); + io_write(sd, REG_INT_FLG_CLR_AUDIO, source); + + /* reset audio FIFO on FIFO pointer error or audio mute */ + if (source & MASK_ERROR_FIFO_PT || + source & MASK_MUTE_FLG) { + /* audio reset audio FIFO */ + reg = io_read(sd, REG_SUS_STATUS); + if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) { + reg = io_read(sd, REG_HDMI_INFO_RST); + reg |= MASK_SR_FIFO_FIFO_CTRL; + io_write(sd, REG_HDMI_INFO_RST, reg); + reg &= ~MASK_SR_FIFO_FIFO_CTRL; + io_write(sd, REG_HDMI_INFO_RST, reg); + /* reset channel status IT if present */ + source &= ~(MASK_CH_STATE); + } + } + if (source & MASK_AUDIO_FREQ_FLG) { + static const int freq[] = { + 0, 32000, 44100, 48000, 88200, 96000, 176400, 192000 + }; + + reg = io_read(sd, REG_AUDIO_FREQ); + state->audio_samplerate = freq[reg & 7]; + v4l_info(state->client, "Audio Frequency Change: %dHz\n", + state->audio_samplerate); + } + if (source & MASK_AUDIO_FLG) { + reg = io_read(sd, REG_AUDIO_FLAGS); + if (reg & BIT(AUDCFG_TYPE_DST)) + state->audio_type = AUDCFG_TYPE_DST; + if (reg & BIT(AUDCFG_TYPE_OBA)) + state->audio_type = AUDCFG_TYPE_OBA; + if (reg & BIT(AUDCFG_TYPE_HBR)) + state->audio_type = AUDCFG_TYPE_HBR; + if (reg & BIT(AUDCFG_TYPE_PCM)) + state->audio_type = AUDCFG_TYPE_PCM; + v4l_info(state->client, "Audio Type: %s\n", + audtype_names[state->audio_type]); + } +} + +static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags) +{ + struct v4l2_subdev *sd = &state->sd; + u8 reg, source; + + source = io_read(sd, REG_INT_FLG_CLR_HDCP); + io_write(sd, REG_INT_FLG_CLR_HDCP, source); + + /* reset MTP in use flag if set */ + if (source & MASK_HDCP_MTP) + state->mptrw_in_progress = 0; + if (source & MASK_STATE_C5) { + /* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */ + reg = io_read(sd, REG_INT_MASK_TOP); + reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); + io_write(sd, REG_INT_MASK_TOP, reg); + *flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); + } +} + +static irqreturn_t tda1997x_isr_thread(int irq, void *d) +{ + struct tda1997x_state *state = d; + struct v4l2_subdev *sd = &state->sd; + u8 flags; + + mutex_lock(&state->lock); + do { + /* read interrupt flags */ + flags = io_read(sd, REG_INT_FLG_CLR_TOP); + if (flags == 0) + break; + + /* SUS interrupt source (Input activity events) */ + if (flags & INTERRUPT_SUS) + tda1997x_irq_sus(state, &flags); + /* DDC interrupt source (Display Data Channel) */ + else if (flags & INTERRUPT_DDC) + tda1997x_irq_ddc(state, &flags); + /* RATE interrupt source (Digital Input activity) */ + else if (flags & INTERRUPT_RATE) + tda1997x_irq_rate(state, &flags); + /* Infoframe change interrupt */ + else if (flags & INTERRUPT_INFO) + tda1997x_irq_info(state, &flags); + /* Audio interrupt source: + * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err + */ + else if (flags & INTERRUPT_AUDIO) + tda1997x_irq_audio(state, &flags); + /* HDCP interrupt source (content protection) */ + if (flags & INTERRUPT_HDCP) + tda1997x_irq_hdcp(state, &flags); + } while (flags != 0); + mutex_unlock(&state->lock); + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * v4l2_subdev_video_ops + */ + +static int +tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct tda1997x_state *state = to_state(sd); + u32 vper; + u16 hper; + u16 hsper; + + mutex_lock(&state->lock); + vper = io_read24(sd, REG_V_PER) & MASK_VPER; + hper = io_read16(sd, REG_H_PER) & MASK_HPER; + hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; + /* + * The tda1997x supports A/B inputs but only a single output. + * The irq handler monitors for timing changes on both inputs and + * sets the input_detect array to 0|1 depending on signal presence. + * I believe selection of A vs B is automatic. + * + * The vper/hper/hsper registers provide the frame period, line period + * and horiz sync period (units of MCLK clock cycles (27MHz)) and + * testing shows these values to be random if no signal is present + * or locked. + */ + v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n", + state->input_detect[0], state->input_detect[1], + vper, hper, hsper); + if (!state->input_detect[0] && !state->input_detect[1]) + *status = V4L2_IN_ST_NO_SIGNAL; + else if (!vper || !hper || !hsper) + *status = V4L2_IN_ST_NO_SYNC; + else + *status = 0; + mutex_unlock(&state->lock); + + return 0; +}; + +static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s\n", __func__); + + if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) + return 0; /* no changes */ + + if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap, + NULL, NULL)) + return -ERANGE; + + mutex_lock(&state->lock); + state->timings = *timings; + /* setup frame detection window and VHREF timing generator */ + tda1997x_configure_vhref(sd); + /* configure colorspace conversion */ + tda1997x_configure_csc(sd); + mutex_unlock(&state->lock); + + return 0; +} + +static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s\n", __func__); + mutex_lock(&state->lock); + *timings = state->timings; + mutex_unlock(&state->lock); + + return 0; +} + +static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s\n", __func__); + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + mutex_lock(&state->lock); + tda1997x_detect_std(state, timings); + mutex_unlock(&state->lock); + + return 0; +} + +static const struct v4l2_subdev_video_ops tda1997x_video_ops = { + .g_input_status = tda1997x_g_input_status, + .s_dv_timings = tda1997x_s_dv_timings, + .g_dv_timings = tda1997x_g_dv_timings, + .query_dv_timings = tda1997x_query_dv_timings, +}; + + +/* ----------------------------------------------------------------------------- + * v4l2_subdev_pad_ops + */ + +static int tda1997x_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct tda1997x_state *state = to_state(sd); + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mf->code = state->mbus_codes[0]; + + return 0; +} + +static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s %d\n", __func__, code->index); + if (code->index >= ARRAY_SIZE(state->mbus_codes)) + return -EINVAL; + + if (!state->mbus_codes[code->index]) + return -EINVAL; + + code->code = state->mbus_codes[code->index]; + + return 0; +} + +static void tda1997x_fill_format(struct tda1997x_state *state, + struct v4l2_mbus_framefmt *format) +{ + const struct v4l2_bt_timings *bt; + + memset(format, 0, sizeof(*format)); + bt = &state->timings.bt; + format->width = bt->width; + format->height = bt->height; + format->colorspace = state->colorimetry.colorspace; + format->field = (bt->interlaced) ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_NONE; +} + +static int tda1997x_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s pad=%d which=%d\n", + __func__, format->pad, format->which); + + tda1997x_fill_format(state, &format->format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + format->format.code = fmt->code; + } else + format->format.code = state->mbus_code; + + return 0; +} + +static int tda1997x_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tda1997x_state *state = to_state(sd); + u32 code = 0; + int i; + + v4l_dbg(1, debug, state->client, "%s pad=%d which=%d fmt=0x%x\n", + __func__, format->pad, format->which, format->format.code); + + for (i = 0; i < ARRAY_SIZE(state->mbus_codes); i++) { + if (format->format.code == state->mbus_codes[i]) { + code = state->mbus_codes[i]; + break; + } + } + if (!code) + code = state->mbus_codes[0]; + + tda1997x_fill_format(state, &format->format); + format->format.code = code; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + *fmt = format->format; + } else { + int ret = tda1997x_setup_format(state, format->format.code); + + if (ret) + return ret; + /* mbus_code has changed - re-configure csc/vidout */ + tda1997x_configure_csc(sd); + tda1997x_configure_vidout(state); + } + + return 0; +} + +static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct tda1997x_state *state = to_state(sd); + + v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); + memset(edid->reserved, 0, sizeof(edid->reserved)); + + if (edid->start_block == 0 && edid->blocks == 0) { + edid->blocks = state->edid.blocks; + return 0; + } + + if (!state->edid.present) + return -ENODATA; + + if (edid->start_block >= state->edid.blocks) + return -EINVAL; + + if (edid->start_block + edid->blocks > state->edid.blocks) + edid->blocks = state->edid.blocks - edid->start_block; + + memcpy(edid->edid, state->edid.edid + edid->start_block * 128, + edid->blocks * 128); + + return 0; +} + +static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct tda1997x_state *state = to_state(sd); + int i; + + v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad); + memset(edid->reserved, 0, sizeof(edid->reserved)); + + if (edid->start_block != 0) + return -EINVAL; + + if (edid->blocks == 0) { + state->edid.blocks = 0; + state->edid.present = 0; + tda1997x_disable_edid(sd); + return 0; + } + + if (edid->blocks > 2) { + edid->blocks = 2; + return -E2BIG; + } + + tda1997x_disable_edid(sd); + + /* write base EDID */ + for (i = 0; i < 128; i++) + io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]); + + /* write CEA Extension */ + for (i = 0; i < 128; i++) + io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); + + tda1997x_enable_edid(sd); + + return 0; +} + +static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = tda1997x_dv_timings_cap; + return 0; +} + +static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap, + NULL, NULL); +} + +static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { + .init_cfg = tda1997x_init_cfg, + .enum_mbus_code = tda1997x_enum_mbus_code, + .get_fmt = tda1997x_get_format, + .set_fmt = tda1997x_set_format, + .get_edid = tda1997x_get_edid, + .set_edid = tda1997x_set_edid, + .dv_timings_cap = tda1997x_get_dv_timings_cap, + .enum_dv_timings = tda1997x_enum_dv_timings, +}; + +/* ----------------------------------------------------------------------------- + * v4l2_subdev_core_ops + */ + +static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) +{ + struct tda1997x_state *state = to_state(sd); + union hdmi_infoframe frame; + u8 buffer[40]; + int len, err; + + /* read data */ + len = io_readn(sd, addr, sizeof(buffer), buffer); + v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); + err = hdmi_infoframe_unpack(&frame, buffer); + if (err) { + v4l_err(state->client, + "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", + len, addr, buffer[0]); + return err; + } + hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame); + + return 0; +} + +static int tda1997x_log_status(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + struct v4l2_dv_timings timings; + struct hdmi_avi_infoframe *avi = &state->avi_infoframe; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip: %s N%d\n", state->info->name, + state->chip_revision + 1); + v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no"); + + v4l2_info(sd, "-----Signal status-----\n"); + v4l2_info(sd, "Cable detected (+5V power): %s\n", + tda1997x_detect_tx_5v(sd) ? "yes" : "no"); + v4l2_info(sd, "HPD detected: %s\n", + tda1997x_detect_tx_hpd(sd) ? "yes" : "no"); + + v4l2_info(sd, "-----Video Timings-----\n"); + switch (tda1997x_detect_std(state, &timings)) { + case -ENOLINK: + v4l2_info(sd, "No video detected\n"); + break; + case -ERANGE: + v4l2_info(sd, "Invalid signal detected\n"); + break; + } + v4l2_print_dv_timings(sd->name, "Configured format: ", + &state->timings, true); + + v4l2_info(sd, "-----Color space-----\n"); + v4l2_info(sd, "Input color space: %s %s %s", + hdmi_colorspace_names[avi->colorspace], + (avi->colorspace == HDMI_COLORSPACE_RGB) ? "" : + hdmi_colorimetry_names[avi->colorimetry], + v4l2_quantization_names[state->colorimetry.quantization]); + v4l2_info(sd, "Output color space: %s", + vidfmt_names[state->vid_fmt]); + v4l2_info(sd, "Color space conversion: %s", state->conv ? + state->conv->name : "None"); + + v4l2_info(sd, "-----Audio-----\n"); + if (state->audio_channels) { + v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels, + state->audio_samplerate); + } else { + v4l2_info(sd, "audio: none\n"); + } + + v4l2_info(sd, "-----Infoframes-----\n"); + tda1997x_log_infoframe(sd, AUD_IF); + tda1997x_log_infoframe(sd, SPD_IF); + tda1997x_log_infoframe(sd, AVI_IF); + + return 0; +} + +static int tda1997x_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops tda1997x_core_ops = { + .log_status = tda1997x_log_status, + .subscribe_event = tda1997x_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * v4l2_subdev_ops + */ + +static const struct v4l2_subdev_ops tda1997x_subdev_ops = { + .core = &tda1997x_core_ops, + .video = &tda1997x_video_ops, + .pad = &tda1997x_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * v4l2_controls + */ + +static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct tda1997x_state *state = to_state(sd); + + switch (ctrl->id) { + /* allow overriding the default RGB quantization range */ + case V4L2_CID_DV_RX_RGB_RANGE: + state->rgb_quantization_range = ctrl->val; + set_rgb_quantization_range(state); + tda1997x_configure_csc(sd); + return 0; + } + + return -EINVAL; +}; + +static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct tda1997x_state *state = to_state(sd); + + if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) { + ctrl->val = state->avi_infoframe.content_type; + return 0; + } + return -EINVAL; +}; + +static const struct v4l2_ctrl_ops tda1997x_ctrl_ops = { + .s_ctrl = tda1997x_s_ctrl, + .g_volatile_ctrl = tda1997x_g_volatile_ctrl, +}; + +static int tda1997x_core_init(struct v4l2_subdev *sd) +{ + struct tda1997x_state *state = to_state(sd); + struct tda1997x_platform_data *pdata = &state->pdata; + u8 reg; + int i; + + /* disable HPD */ + io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL); + if (state->chip_revision == 0) { + io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); + io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); + } + + /* reset infoframe at end of start-up-sequencer */ + io_write(sd, REG_SUS_SET_RGB2, 0x06); + io_write(sd, REG_SUS_SET_RGB3, 0x06); + + /* Enable TMDS pull-ups */ + io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT | + RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A); + + /* enable sync measurement timing */ + tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04); + /* adjust CEC clock divider */ + tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03); + tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); + io_write(sd, REG_TIMER_D, 0x54); + /* enable power switch */ + reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff); + reg |= 0x20; + tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg); + mdelay(50); + + /* read the chip version */ + reg = io_read(sd, REG_VERSION); + /* get the chip configuration */ + reg = io_read(sd, REG_CMTP_REG10); + + /* enable interrupts we care about */ + io_write(sd, REG_INT_MASK_TOP, + INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO | + INTERRUPT_RATE | INTERRUPT_SUS); + /* config_mtp,fmt,sus_end,sus_st */ + io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END); + /* rate stability change for inputs A/B */ + io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST); + /* aud,spd,avi*/ + io_write(sd, REG_INT_MASK_INFO, + MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF); + /* audio_freq,audio_flg,mute_flg,fifo_err */ + io_write(sd, REG_INT_MASK_AUDIO, + MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG | + MASK_ERROR_FIFO_PT); + /* HDCP C5 state reached */ + io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5); + /* 5V detect and HDP pulse end */ + io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V); + /* don't care about AFE/MODE */ + io_write(sd, REG_INT_MASK_AFE, 0); + io_write(sd, REG_INT_MASK_MODE, 0); + + /* clear all interrupts */ + io_write(sd, REG_INT_FLG_CLR_TOP, 0xff); + io_write(sd, REG_INT_FLG_CLR_SUS, 0xff); + io_write(sd, REG_INT_FLG_CLR_DDC, 0xff); + io_write(sd, REG_INT_FLG_CLR_RATE, 0xff); + io_write(sd, REG_INT_FLG_CLR_MODE, 0xff); + io_write(sd, REG_INT_FLG_CLR_INFO, 0xff); + io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff); + io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff); + io_write(sd, REG_INT_FLG_CLR_AFE, 0xff); + + /* init TMDS equalizer */ + if (state->chip_revision == 0) + io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT); + io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE); + io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE); + if (state->chip_revision == 0) + io_write(sd, REG_WDL_CFG, WDL_CFG_VAL); + /* DC filter */ + io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); + /* disable test pattern */ + io_write(sd, REG_SVC_MODE, 0x00); + /* update HDMI INFO CTRL */ + io_write(sd, REG_INFO_CTRL, 0xff); + /* write HDMI INFO EXCEED value */ + io_write(sd, REG_INFO_EXCEED, 3); + + if (state->chip_revision == 0) + tda1997x_reset_n1(state); + + /* + * No HDCP acknowledge when HDCP is disabled + * and reset SUS to force format detection + */ + tda1997x_hdmi_info_reset(sd, NACK_HDCP, true); + + /* Set HPD low */ + tda1997x_manual_hpd(sd, HPD_LOW_BP); + + /* Configure receiver capabilities */ + io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); + + /* Configure HDMI: Auto HDCP mode, packet controlled mute */ + reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT; + reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT; + io_write(sd, REG_HDMI_CTRL, reg); + + /* reset start-up-sequencer to force format detection */ + tda1997x_hdmi_info_reset(sd, 0, true); + + /* disable matrix conversion */ + reg = io_read(sd, REG_VDP_CTRL); + reg |= VDP_CTRL_MATRIX_BP; + io_write(sd, REG_VDP_CTRL, reg); + + /* set video output mode */ + tda1997x_configure_vidout(state); + + /* configure video output port */ + for (i = 0; i < 9; i++) { + v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i, + pdata->vidout_port_cfg[i]); + io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]); + } + + /* configure audio output port */ + tda1997x_configure_audout(sd, 0); + + /* configure audio clock freq */ + switch (pdata->audout_mclk_fs) { + case 512: + reg = AUDIO_CLOCK_SEL_512FS; + break; + case 256: + reg = AUDIO_CLOCK_SEL_256FS; + break; + case 128: + reg = AUDIO_CLOCK_SEL_128FS; + break; + case 64: + reg = AUDIO_CLOCK_SEL_64FS; + break; + case 32: + reg = AUDIO_CLOCK_SEL_32FS; + break; + default: + reg = AUDIO_CLOCK_SEL_16FS; + break; + } + io_write(sd, REG_AUDIO_CLOCK, reg); + + /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ + tda1997x_hdmi_info_reset(sd, RESET_AI, false); + /* reset infoframe */ + tda1997x_hdmi_info_reset(sd, RESET_IF, false); + /* reset audio infoframes */ + tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false); + /* reset gamut */ + tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false); + + /* get initial HDMI status */ + state->hdmi_status = io_read(sd, REG_HDMI_FLAGS); + + return 0; +} + +static int tda1997x_set_power(struct tda1997x_state *state, bool on) +{ + int ret = 0; + + if (on) { + ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES, + state->supplies); + msleep(300); + } else { + ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, + state->supplies); + } + + return ret; +} + +static const struct i2c_device_id tda1997x_i2c_id[] = { + {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, + {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); + +static const struct of_device_id tda1997x_of_id[] __maybe_unused = { + { .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] }, + { .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] }, + { }, +}; +MODULE_DEVICE_TABLE(of, tda1997x_of_id); + +static int tda1997x_parse_dt(struct tda1997x_state *state) +{ + struct tda1997x_platform_data *pdata = &state->pdata; + struct v4l2_fwnode_endpoint bus_cfg; + struct device_node *ep; + struct device_node *np; + unsigned int flags; + const char *str; + int ret; + u32 v; + + /* + * setup default values: + * - HREF: active high from start to end of row + * - VS: Vertical Sync active high at beginning of frame + * - DE: Active high when data valid + * - A_CLK: 128*Fs + */ + pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF; + pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI; + pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF; + + np = state->client->dev.of_node; + ep = of_graph_get_next_endpoint(np, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); + if (ret) { + of_node_put(ep); + return ret; + } + of_node_put(ep); + pdata->vidout_bus_type = bus_cfg.bus_type; + + /* polarity of HS/VS/DE */ + flags = bus_cfg.bus.parallel.flags; + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + pdata->vidout_inv_hs = 1; + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + pdata->vidout_inv_vs = 1; + if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) + pdata->vidout_inv_de = 1; + pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width; + + /* video output port config */ + ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg"); + if (ret > 0) { + u32 reg, val, i; + + for (i = 0; i < ret / 2 && i < 9; i++) { + of_property_read_u32_index(np, "nxp,vidout-portcfg", + i * 2, ®); + of_property_read_u32_index(np, "nxp,vidout-portcfg", + i * 2 + 1, &val); + if (reg < 9) + pdata->vidout_port_cfg[reg] = val; + } + } else { + v4l_err(state->client, "nxp,vidout-portcfg missing\n"); + return -EINVAL; + } + + /* default to channel layout dictated by packet header */ + pdata->audout_layoutauto = true; + + pdata->audout_format = AUDFMT_TYPE_DISABLED; + if (!of_property_read_string(np, "nxp,audout-format", &str)) { + if (strcmp(str, "i2s") == 0) + pdata->audout_format = AUDFMT_TYPE_I2S; + else if (strcmp(str, "spdif") == 0) + pdata->audout_format = AUDFMT_TYPE_SPDIF; + else { + v4l_err(state->client, "nxp,audout-format invalid\n"); + return -EINVAL; + } + if (!of_property_read_u32(np, "nxp,audout-layout", &v)) { + switch (v) { + case 0: + case 1: + break; + default: + v4l_err(state->client, + "nxp,audout-layout invalid\n"); + return -EINVAL; + } + pdata->audout_layout = v; + } + if (!of_property_read_u32(np, "nxp,audout-width", &v)) { + switch (v) { + case 16: + case 32: + break; + default: + v4l_err(state->client, + "nxp,audout-width invalid\n"); + return -EINVAL; + } + pdata->audout_width = v; + } + if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) { + switch (v) { + case 512: + case 256: + case 128: + case 64: + case 32: + case 16: + break; + default: + v4l_err(state->client, + "nxp,audout-mclk-fs invalid\n"); + return -EINVAL; + } + pdata->audout_mclk_fs = v; + } + } + + return 0; +} + +static int tda1997x_get_regulators(struct tda1997x_state *state) +{ + int i; + + for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++) + state->supplies[i].supply = tda1997x_supply_name[i]; + + return devm_regulator_bulk_get(&state->client->dev, + TDA1997X_NUM_SUPPLIES, + state->supplies); +} + +static int tda1997x_identify_module(struct tda1997x_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + enum tda1997x_type type; + u8 reg; + + /* Read chip configuration*/ + reg = io_read(sd, REG_CMTP_REG10); + state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ + state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ + state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ + state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ + switch ((reg >> 4) & 0x03) { + case 0x00: + type = TDA19971; + break; + case 0x02: + case 0x03: + type = TDA19973; + break; + default: + dev_err(&state->client->dev, "unsupported chip ID\n"); + return -EIO; + } + if (state->info->type != type) { + dev_err(&state->client->dev, "chip id mismatch\n"); + return -EIO; + } + + /* read chip revision */ + state->chip_revision = io_read(sd, REG_CMTP_REG11); + + return 0; +} + +static const struct media_entity_operations tda1997x_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + + +/* ----------------------------------------------------------------------------- + * HDMI Audio Codec + */ + +/* refine sample-rate based on HDMI source */ +static int tda1997x_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai); + struct snd_soc_codec *codec = dai->codec; + struct snd_pcm_runtime *rtd = substream->runtime; + int rate, err; + + rate = state->audio_samplerate; + err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE, + rate, rate); + if (err < 0) { + dev_err(codec->dev, "failed to constrain samplerate to %dHz\n", + rate); + return err; + } + dev_info(codec->dev, "set samplerate constraint to %dHz\n", rate); + + return 0; +} + +static const struct snd_soc_dai_ops tda1997x_dai_ops = { + .startup = tda1997x_pcm_startup, +}; + +static struct snd_soc_dai_driver tda1997x_audio_dai = { + .name = "tda1997x", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + }, + .ops = &tda1997x_dai_ops, +}; + +static int tda1997x_codec_probe(struct snd_soc_codec *codec) +{ + return 0; +} + +static int tda1997x_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static struct snd_soc_codec_driver tda1997x_codec_driver = { + .probe = tda1997x_codec_probe, + .remove = tda1997x_codec_remove, + .reg_word_size = sizeof(u16), +}; + +static int tda1997x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tda1997x_state *state; + struct tda1997x_platform_data *pdata; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + struct v4l2_ctrl *ctrl; + static const struct v4l2_dv_timings cea1920x1080 = + V4L2_DV_BT_CEA_1920X1080P60; + u32 *mbus_codes; + int i, ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->client = client; + pdata = &state->pdata; + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { + const struct of_device_id *oid; + + oid = of_match_node(tda1997x_of_id, client->dev.of_node); + state->info = oid->data; + + ret = tda1997x_parse_dt(state); + if (ret < 0) { + v4l_err(client, "DT parsing error\n"); + goto err_free_state; + } + } else if (client->dev.platform_data) { + struct tda1997x_platform_data *pdata = + client->dev.platform_data; + state->info = + (const struct tda1997x_chip_info *)id->driver_data; + state->pdata = *pdata; + } else { + v4l_err(client, "No platform data\n"); + ret = -ENODEV; + goto err_free_state; + } + + ret = tda1997x_get_regulators(state); + if (ret) + goto err_free_state; + + ret = tda1997x_set_power(state, 1); + if (ret) + goto err_free_state; + + mutex_init(&state->page_lock); + mutex_init(&state->lock); + state->page = 0xff; + + INIT_DELAYED_WORK(&state->delayed_work_enable_hpd, + tda1997x_delayed_work_enable_hpd); + + /* set video format based on chip and bus width */ + ret = tda1997x_identify_module(state); + if (ret) + goto err_free_mutex; + + /* initialize subdev */ + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops); + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + id->name, i2c_adapter_id(client->adapter), + client->addr); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->entity.function = MEDIA_ENT_F_DTV_DECODER; + sd->entity.ops = &tda1997x_media_ops; + + /* set allowed mbus modes based on chip, bus-type, and bus-width */ + i = 0; + mbus_codes = state->mbus_codes; + switch (state->info->type) { + case TDA19973: + switch (pdata->vidout_bus_type) { + case V4L2_MBUS_PARALLEL: + switch (pdata->vidout_bus_width) { + case 36: + mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36; + /* fall-through */ + case 24: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + break; + } + break; + case V4L2_MBUS_BT656: + switch (pdata->vidout_bus_width) { + case 36: + case 24: + case 12: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; + break; + } + break; + default: + break; + } + break; + case TDA19971: + switch (pdata->vidout_bus_type) { + case V4L2_MBUS_PARALLEL: + switch (pdata->vidout_bus_width) { + case 24: + mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24; + mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24; + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + /* fall through */ + case 20: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + /* fall through */ + case 16: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + break; + } + break; + case V4L2_MBUS_BT656: + switch (pdata->vidout_bus_width) { + case 24: + case 20: + case 16: + case 12: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; + /* fall through */ + case 10: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; + /* fall through */ + case 8: + mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; + break; + } + break; + default: + break; + } + break; + } + if (WARN_ON(i > ARRAY_SIZE(state->mbus_codes))) { + ret = -EINVAL; + goto err_free_mutex; + } + + /* default format */ + tda1997x_setup_format(state, state->mbus_codes[0]); + state->timings = cea1920x1080; + + /* + * default to SRGB full range quantization + * (in case we don't get an infoframe such as DVI signal + */ + state->colorimetry.colorspace = V4L2_COLORSPACE_SRGB; + state->colorimetry.quantization = V4L2_QUANTIZATION_FULL_RANGE; + + /* disable/reset HDCP to get correct I2C access to Rx HDMI */ + io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); + + /* + * if N2 version, reset compdel_bp as it may generate some small pixel + * shifts in case of embedded sync/or delay lower than 4 + */ + if (state->chip_revision != 0) { + io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00); + io_write(sd, REG_VDP_CTRL, 0x1f); + } + + v4l_info(client, "NXP %s N%d detected\n", state->info->name, + state->chip_revision + 1); + v4l_info(client, "video: %dbit %s %d formats available\n", + pdata->vidout_bus_width, + (pdata->vidout_bus_type == V4L2_MBUS_PARALLEL) ? + "parallel" : "BT656", + i); + if (pdata->audout_format) { + v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n", + pdata->audout_layout ? 2 : 8, + audfmt_names[pdata->audout_format], + pdata->audout_layout, + pdata->audout_mclk_fs); + } + + ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03); + state->client_cec = i2c_new_dummy(client->adapter, ret); + v4l_info(client, "CEC slave address 0x%02x\n", ret); + + ret = tda1997x_core_init(sd); + if (ret) + goto err_free_mutex; + + /* control handlers */ + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 3); + ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops, + V4L2_CID_DV_RX_IT_CONTENT_TYPE, + V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, + V4L2_DV_IT_CONTENT_TYPE_NO_ITC); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + /* custom controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, + &tda1997x_ctrl_ops, + V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, + V4L2_DV_RGB_RANGE_AUTO); + state->sd.ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + goto err_free_handler; + } + v4l2_ctrl_handler_setup(hdl); + + /* initialize source pads */ + state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS, + state->pads); + if (ret) { + v4l_err(client, "failed entity_init: %d", ret); + goto err_free_mutex; + } + + ret = v4l2_async_register_subdev(sd); + if (ret) + goto err_free_media; + + /* register audio DAI */ + if (pdata->audout_format) { + u64 formats; + + if (pdata->audout_width == 32) + formats = SNDRV_PCM_FMTBIT_S32_LE; + else + formats = SNDRV_PCM_FMTBIT_S16_LE; + tda1997x_audio_dai.capture.formats = formats; + ret = snd_soc_register_codec(&state->client->dev, + &tda1997x_codec_driver, + &tda1997x_audio_dai, 1); + if (ret) { + dev_err(&client->dev, "register audio codec failed\n"); + goto err_free_media; + } + dev_set_drvdata(&state->client->dev, state); + v4l_info(state->client, "registered audio codec\n"); + } + + /* request irq */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, tda1997x_isr_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + KBUILD_MODNAME, state); + if (ret) { + v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret); + goto err_free_media; + } + + return 0; + +err_free_media: + media_entity_cleanup(&sd->entity); +err_free_handler: + v4l2_ctrl_handler_free(&state->hdl); +err_free_mutex: + cancel_delayed_work(&state->delayed_work_enable_hpd); + mutex_destroy(&state->page_lock); + mutex_destroy(&state->lock); +err_free_state: + kfree(state); + dev_err(&client->dev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static int tda1997x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tda1997x_state *state = to_state(sd); + struct tda1997x_platform_data *pdata = &state->pdata; + + if (pdata->audout_format) { + snd_soc_unregister_codec(&client->dev); + mutex_destroy(&state->audio_lock); + } + + disable_irq(state->client->irq); + tda1997x_power_mode(state, 0); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&state->hdl); + regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); + i2c_unregister_device(state->client_cec); + cancel_delayed_work(&state->delayed_work_enable_hpd); + mutex_destroy(&state->page_lock); + mutex_destroy(&state->lock); + + kfree(state); + + return 0; +} + +static struct i2c_driver tda1997x_i2c_driver = { + .driver = { + .name = "tda1997x", + .of_match_table = of_match_ptr(tda1997x_of_id), + }, + .probe = tda1997x_probe, + .remove = tda1997x_remove, + .id_table = tda1997x_i2c_id, +}; + +module_i2c_driver(tda1997x_i2c_driver); + +MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); +MODULE_DESCRIPTION("TDA1997X HDMI Receiver driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/tda1997x_regs.h b/drivers/media/i2c/tda1997x_regs.h new file mode 100644 index 000000000000..f55dfc423a86 --- /dev/null +++ b/drivers/media/i2c/tda1997x_regs.h @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Gateworks Corporation + */ + +/* Page 0x00 - General Control */ +#define REG_VERSION 0x0000 +#define REG_INPUT_SEL 0x0001 +#define REG_SVC_MODE 0x0002 +#define REG_HPD_MAN_CTRL 0x0003 +#define REG_RT_MAN_CTRL 0x0004 +#define REG_STANDBY_SOFT_RST 0x000A +#define REG_HDMI_SOFT_RST 0x000B +#define REG_HDMI_INFO_RST 0x000C +#define REG_INT_FLG_CLR_TOP 0x000E +#define REG_INT_FLG_CLR_SUS 0x000F +#define REG_INT_FLG_CLR_DDC 0x0010 +#define REG_INT_FLG_CLR_RATE 0x0011 +#define REG_INT_FLG_CLR_MODE 0x0012 +#define REG_INT_FLG_CLR_INFO 0x0013 +#define REG_INT_FLG_CLR_AUDIO 0x0014 +#define REG_INT_FLG_CLR_HDCP 0x0015 +#define REG_INT_FLG_CLR_AFE 0x0016 +#define REG_INT_MASK_TOP 0x0017 +#define REG_INT_MASK_SUS 0x0018 +#define REG_INT_MASK_DDC 0x0019 +#define REG_INT_MASK_RATE 0x001A +#define REG_INT_MASK_MODE 0x001B +#define REG_INT_MASK_INFO 0x001C +#define REG_INT_MASK_AUDIO 0x001D +#define REG_INT_MASK_HDCP 0x001E +#define REG_INT_MASK_AFE 0x001F +#define REG_DETECT_5V 0x0020 +#define REG_SUS_STATUS 0x0021 +#define REG_V_PER 0x0022 +#define REG_H_PER 0x0025 +#define REG_HS_WIDTH 0x0027 +#define REG_FMT_H_TOT 0x0029 +#define REG_FMT_H_ACT 0x002b +#define REG_FMT_H_FRONT 0x002d +#define REG_FMT_H_SYNC 0x002f +#define REG_FMT_H_BACK 0x0031 +#define REG_FMT_V_TOT 0x0033 +#define REG_FMT_V_ACT 0x0035 +#define REG_FMT_V_FRONT_F1 0x0037 +#define REG_FMT_V_FRONT_F2 0x0038 +#define REG_FMT_V_SYNC 0x0039 +#define REG_FMT_V_BACK_F1 0x003a +#define REG_FMT_V_BACK_F2 0x003b +#define REG_FMT_DE_ACT 0x003c +#define REG_RATE_CTRL 0x0040 +#define REG_CLK_MIN_RATE 0x0043 +#define REG_CLK_MAX_RATE 0x0046 +#define REG_CLK_A_STATUS 0x0049 +#define REG_CLK_A_RATE 0x004A +#define REG_DRIFT_CLK_A_REG 0x004D +#define REG_CLK_B_STATUS 0x004E +#define REG_CLK_B_RATE 0x004F +#define REG_DRIFT_CLK_B_REG 0x0052 +#define REG_HDCP_CTRL 0x0060 +#define REG_HDCP_KDS 0x0061 +#define REG_HDCP_BCAPS 0x0063 +#define REG_HDCP_KEY_CTRL 0x0064 +#define REG_INFO_CTRL 0x0076 +#define REG_INFO_EXCEED 0x0077 +#define REG_PIX_REPEAT 0x007B +#define REG_AUDIO_PATH 0x007C +#define REG_AUDCFG 0x007D +#define REG_AUDIO_OUT_ENABLE 0x007E +#define REG_AUDIO_OUT_HIZ 0x007F +#define REG_VDP_CTRL 0x0080 +#define REG_VDP_MATRIX 0x0081 +#define REG_VHREF_CTRL 0x00A0 +#define REG_PXCNT_PR 0x00A2 +#define REG_PXCNT_NPIX 0x00A4 +#define REG_LCNT_PR 0x00A6 +#define REG_LCNT_NLIN 0x00A8 +#define REG_HREF_S 0x00AA +#define REG_HREF_E 0x00AC +#define REG_HS_S 0x00AE +#define REG_HS_E 0x00B0 +#define REG_VREF_F1_S 0x00B2 +#define REG_VREF_F1_WIDTH 0x00B4 +#define REG_VREF_F2_S 0x00B5 +#define REG_VREF_F2_WIDTH 0x00B7 +#define REG_VS_F1_LINE_S 0x00B8 +#define REG_VS_F1_LINE_WIDTH 0x00BA +#define REG_VS_F2_LINE_S 0x00BB +#define REG_VS_F2_LINE_WIDTH 0x00BD +#define REG_VS_F1_PIX_S 0x00BE +#define REG_VS_F1_PIX_E 0x00C0 +#define REG_VS_F2_PIX_S 0x00C2 +#define REG_VS_F2_PIX_E 0x00C4 +#define REG_FREF_F1_S 0x00C6 +#define REG_FREF_F2_S 0x00C8 +#define REG_FDW_S 0x00ca +#define REG_FDW_E 0x00cc +#define REG_BLK_GY 0x00da +#define REG_BLK_BU 0x00dc +#define REG_BLK_RV 0x00de +#define REG_FILTERS_CTRL 0x00e0 +#define REG_DITHERING_CTRL 0x00E9 +#define REG_OF 0x00EA +#define REG_PCLK 0x00EB +#define REG_HS_HREF 0x00EC +#define REG_VS_VREF 0x00ED +#define REG_DE_FREF 0x00EE +#define REG_VP35_32_CTRL 0x00EF +#define REG_VP31_28_CTRL 0x00F0 +#define REG_VP27_24_CTRL 0x00F1 +#define REG_VP23_20_CTRL 0x00F2 +#define REG_VP19_16_CTRL 0x00F3 +#define REG_VP15_12_CTRL 0x00F4 +#define REG_VP11_08_CTRL 0x00F5 +#define REG_VP07_04_CTRL 0x00F6 +#define REG_VP03_00_CTRL 0x00F7 +#define REG_CURPAGE_00H 0xFF + +#define MASK_VPER 0x3fffff +#define MASK_VHREF 0x3fff +#define MASK_HPER 0x0fff +#define MASK_HSWIDTH 0x03ff + +/* HPD Detection */ +#define DETECT_UTIL BIT(7) /* utility of HDMI level */ +#define DETECT_HPD BIT(6) /* HPD of HDMI level */ +#define DETECT_5V_SEL BIT(2) /* 5V present on selected input */ +#define DETECT_5V_B BIT(1) /* 5V present on input B */ +#define DETECT_5V_A BIT(0) /* 5V present on input A */ + +/* Input Select */ +#define INPUT_SEL_RST_FMT BIT(7) /* 1=reset format measurement */ +#define INPUT_SEL_RST_VDP BIT(2) /* 1=reset video data path */ +#define INPUT_SEL_OUT_MODE BIT(1) /* 0=loop 1=bypass */ +#define INPUT_SEL_B BIT(0) /* 0=inputA 1=inputB */ + +/* Service Mode */ +#define SVC_MODE_CLK2_MASK 0xc0 +#define SVC_MODE_CLK2_SHIFT 6 +#define SVC_MODE_CLK2_XTL 0L +#define SVC_MODE_CLK2_XTLDIV2 1L +#define SVC_MODE_CLK2_HDMIX2 3L +#define SVC_MODE_CLK1_MASK 0x30 +#define SVC_MODE_CLK1_SHIFT 4 +#define SVC_MODE_CLK1_XTAL 0L +#define SVC_MODE_CLK1_XTLDIV2 1L +#define SVC_MODE_CLK1_HDMI 3L +#define SVC_MODE_RAMP BIT(3) /* 0=colorbar 1=ramp */ +#define SVC_MODE_PAL BIT(2) /* 0=NTSC(480i/p) 1=PAL(576i/p) */ +#define SVC_MODE_INT_PROG BIT(1) /* 0=interlaced 1=progressive */ +#define SVC_MODE_SM_ON BIT(0) /* Enable color bars and tone gen */ + +/* HDP Manual Control */ +#define HPD_MAN_CTRL_HPD_PULSE BIT(7) /* HPD Pulse low 110ms */ +#define HPD_MAN_CTRL_5VEN BIT(2) /* Output 5V */ +#define HPD_MAN_CTRL_HPD_B BIT(1) /* Assert HPD High for Input A */ +#define HPD_MAN_CTRL_HPD_A BIT(0) /* Assert HPD High for Input A */ + +/* RT_MAN_CTRL */ +#define RT_MAN_CTRL_RT_AUTO BIT(7) +#define RT_MAN_CTRL_RT BIT(6) +#define RT_MAN_CTRL_RT_B BIT(1) /* enable TMDS pull-up on Input B */ +#define RT_MAN_CTRL_RT_A BIT(0) /* enable TMDS pull-up on Input A */ + +/* VDP_CTRL */ +#define VDP_CTRL_COMPDEL_BP BIT(5) /* bypass compdel */ +#define VDP_CTRL_FORMATTER_BP BIT(4) /* bypass formatter */ +#define VDP_CTRL_PREFILTER_BP BIT(1) /* bypass prefilter */ +#define VDP_CTRL_MATRIX_BP BIT(0) /* bypass matrix conversion */ + +/* REG_VHREF_CTRL */ +#define VHREF_INT_DET BIT(7) /* interlace detect: 1=alt 0=frame */ +#define VHREF_VSYNC_MASK 0x60 +#define VHREF_VSYNC_SHIFT 6 +#define VHREF_VSYNC_AUTO 0L +#define VHREF_VSYNC_FDW 1L +#define VHREF_VSYNC_EVEN 2L +#define VHREF_VSYNC_ODD 3L +#define VHREF_STD_DET_MASK 0x18 +#define VHREF_STD_DET_SHIFT 3 +#define VHREF_STD_DET_PAL 0L +#define VHREF_STD_DET_NTSC 1L +#define VHREF_STD_DET_AUTO 2L +#define VHREF_STD_DET_OFF 3L +#define VHREF_VREF_SRC_STD BIT(2) /* 1=from standard 0=manual */ +#define VHREF_HREF_SRC_STD BIT(1) /* 1=from standard 0=manual */ +#define VHREF_HSYNC_SEL_HS BIT(0) /* 1=HS 0=VS */ + +/* AUDIO_OUT_ENABLE */ +#define AUDIO_OUT_ENABLE_ACLK BIT(5) +#define AUDIO_OUT_ENABLE_WS BIT(4) +#define AUDIO_OUT_ENABLE_AP3 BIT(3) +#define AUDIO_OUT_ENABLE_AP2 BIT(2) +#define AUDIO_OUT_ENABLE_AP1 BIT(1) +#define AUDIO_OUT_ENABLE_AP0 BIT(0) + +/* Prefilter Control */ +#define FILTERS_CTRL_BU_MASK 0x0c +#define FILTERS_CTRL_BU_SHIFT 2 +#define FILTERS_CTRL_RV_MASK 0x03 +#define FILTERS_CTRL_RV_SHIFT 0 +#define FILTERS_CTRL_OFF 0L /* off */ +#define FILTERS_CTRL_2TAP 1L /* 2 Taps */ +#define FILTERS_CTRL_7TAP 2L /* 7 Taps */ +#define FILTERS_CTRL_2_7TAP 3L /* 2/7 Taps */ + +/* PCLK Configuration */ +#define PCLK_DELAY_MASK 0x70 +#define PCLK_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */ +#define PCLK_INV_SHIFT 2 +#define PCLK_SEL_MASK 0x03 /* clock scaler */ +#define PCLK_SEL_SHIFT 0 +#define PCLK_SEL_X1 0L +#define PCLK_SEL_X2 1L +#define PCLK_SEL_DIV2 2L +#define PCLK_SEL_DIV4 3L + +/* Pixel Repeater */ +#define PIX_REPEAT_MASK_UP_SEL 0x30 +#define PIX_REPEAT_MASK_REP 0x0f +#define PIX_REPEAT_SHIFT 4 +#define PIX_REPEAT_CHROMA 1 + +/* Page 0x01 - HDMI info and packets */ +#define REG_HDMI_FLAGS 0x0100 +#define REG_DEEP_COLOR_MODE 0x0101 +#define REG_AUDIO_FLAGS 0x0108 +#define REG_AUDIO_FREQ 0x0109 +#define REG_ACP_PACKET_TYPE 0x0141 +#define REG_ISRC1_PACKET_TYPE 0x0161 +#define REG_ISRC2_PACKET_TYPE 0x0181 +#define REG_GBD_PACKET_TYPE 0x01a1 + +/* HDMI_FLAGS */ +#define HDMI_FLAGS_AUDIO BIT(7) /* Audio packet in last videoframe */ +#define HDMI_FLAGS_HDMI BIT(6) /* HDMI detected */ +#define HDMI_FLAGS_EESS BIT(5) /* EESS detected */ +#define HDMI_FLAGS_HDCP BIT(4) /* HDCP detected */ +#define HDMI_FLAGS_AVMUTE BIT(3) /* AVMUTE */ +#define HDMI_FLAGS_AUD_LAYOUT BIT(2) /* Layout status Audio sample packet */ +#define HDMI_FLAGS_AUD_FIFO_OF BIT(1) /* FIFO read/write pointers crossed */ +#define HDMI_FLAGS_AUD_FIFO_LOW BIT(0) /* FIFO read ptr within 2 of write */ + +/* Page 0x12 - HDMI Extra control and debug */ +#define REG_CLK_CFG 0x1200 +#define REG_CLK_OUT_CFG 0x1201 +#define REG_CFG1 0x1202 +#define REG_CFG2 0x1203 +#define REG_WDL_CFG 0x1210 +#define REG_DELOCK_DELAY 0x1212 +#define REG_PON_OVR_EN 0x12A0 +#define REG_PON_CBIAS 0x12A1 +#define REG_PON_RESCAL 0x12A2 +#define REG_PON_RES 0x12A3 +#define REG_PON_CLK 0x12A4 +#define REG_PON_PLL 0x12A5 +#define REG_PON_EQ 0x12A6 +#define REG_PON_DES 0x12A7 +#define REG_PON_OUT 0x12A8 +#define REG_PON_MUX 0x12A9 +#define REG_MODE_REC_CFG1 0x12F8 +#define REG_MODE_REC_CFG2 0x12F9 +#define REG_MODE_REC_STS 0x12FA +#define REG_AUDIO_LAYOUT 0x12D0 + +#define PON_EN 1 +#define PON_DIS 0 + +/* CLK CFG */ +#define CLK_CFG_INV_OUT_CLK BIT(7) +#define CLK_CFG_INV_BUS_CLK BIT(6) +#define CLK_CFG_SEL_ACLK_EN BIT(1) +#define CLK_CFG_SEL_ACLK BIT(0) +#define CLK_CFG_DIS 0 + +/* Page 0x13 - HDMI Extra control and debug */ +#define REG_DEEP_COLOR_CTRL 0x1300 +#define REG_CGU_DBG_SEL 0x1305 +#define REG_HDCP_DDC_ADDR 0x1310 +#define REG_HDCP_KIDX 0x1316 +#define REG_DEEP_PLL7_BYP 0x1347 +#define REG_HDCP_DE_CTRL 0x1370 +#define REG_HDCP_EP_FILT_CTRL 0x1371 +#define REG_HDMI_CTRL 0x1377 +#define REG_HMTP_CTRL 0x137a +#define REG_TIMER_D 0x13CF +#define REG_SUS_SET_RGB0 0x13E1 +#define REG_SUS_SET_RGB1 0x13E2 +#define REG_SUS_SET_RGB2 0x13E3 +#define REG_SUS_SET_RGB3 0x13E4 +#define REG_SUS_SET_RGB4 0x13E5 +#define REG_MAN_SUS_HDMI_SEL 0x13E8 +#define REG_MAN_HDMI_SET 0x13E9 +#define REG_SUS_CLOCK_GOOD 0x13EF + +/* HDCP DE Control */ +#define HDCP_DE_MODE_MASK 0xc0 /* DE Measurement mode */ +#define HDCP_DE_MODE_SHIFT 6 +#define HDCP_DE_REGEN_EN BIT(5) /* enable regen mode */ +#define HDCP_DE_FILTER_MASK 0x18 /* DE filter sensitivity */ +#define HDCP_DE_FILTER_SHIFT 3 +#define HDCP_DE_COMP_MASK 0x07 /* DE Composition mode */ +#define HDCP_DE_COMP_MIXED 6L +#define HDCP_DE_COMP_OR 5L +#define HDCP_DE_COMP_AND 4L +#define HDCP_DE_COMP_CH3 3L +#define HDCP_DE_COMP_CH2 2L +#define HDCP_DE_COMP_CH1 1L +#define HDCP_DE_COMP_CH0 0L + +/* HDCP EP Filter Control */ +#define HDCP_EP_FIL_CTL_MASK 0x30 +#define HDCP_EP_FIL_CTL_SHIFT 4 +#define HDCP_EP_FIL_VS_MASK 0x0c +#define HDCP_EP_FIL_VS_SHIFT 2 +#define HDCP_EP_FIL_HS_MASK 0x03 +#define HDCP_EP_FIL_HS_SHIFT 0 + +/* HDMI_CTRL */ +#define HDMI_CTRL_MUTE_MASK 0x0c +#define HDMI_CTRL_MUTE_SHIFT 2 +#define HDMI_CTRL_MUTE_AUTO 0L +#define HDMI_CTRL_MUTE_OFF 1L +#define HDMI_CTRL_MUTE_ON 2L +#define HDMI_CTRL_HDCP_MASK 0x03 +#define HDMI_CTRL_HDCP_SHIFT 0 +#define HDMI_CTRL_HDCP_EESS 2L +#define HDMI_CTRL_HDCP_OESS 1L +#define HDMI_CTRL_HDCP_AUTO 0L + +/* CGU_DBG_SEL bits */ +#define CGU_DBG_CLK_SEL_MASK 0x18 +#define CGU_DBG_CLK_SEL_SHIFT 3 +#define CGU_DBG_XO_FRO_SEL BIT(2) +#define CGU_DBG_VDP_CLK_SEL BIT(1) +#define CGU_DBG_PIX_CLK_SEL BIT(0) + +/* REG_MAN_SUS_HDMI_SEL / REG_MAN_HDMI_SET bits */ +#define MAN_DIS_OUT_BUF BIT(7) +#define MAN_DIS_ANA_PATH BIT(6) +#define MAN_DIS_HDCP BIT(5) +#define MAN_DIS_TMDS_ENC BIT(4) +#define MAN_DIS_TMDS_FLOW BIT(3) +#define MAN_RST_HDCP BIT(2) +#define MAN_RST_TMDS_ENC BIT(1) +#define MAN_RST_TMDS_FLOW BIT(0) + +/* Page 0x14 - Audio Extra control and debug */ +#define REG_FIFO_LATENCY_VAL 0x1403 +#define REG_AUDIO_CLOCK 0x1411 +#define REG_TEST_NCTS_CTRL 0x1415 +#define REG_TEST_AUDIO_FREQ 0x1426 +#define REG_TEST_MODE 0x1437 + +/* Audio Clock Configuration */ +#define AUDIO_CLOCK_PLL_PD BIT(7) /* powerdown PLL */ +#define AUDIO_CLOCK_SEL_MASK 0x7f +#define AUDIO_CLOCK_SEL_16FS 0L /* 16*fs */ +#define AUDIO_CLOCK_SEL_32FS 1L /* 32*fs */ +#define AUDIO_CLOCK_SEL_64FS 2L /* 64*fs */ +#define AUDIO_CLOCK_SEL_128FS 3L /* 128*fs */ +#define AUDIO_CLOCK_SEL_256FS 4L /* 256*fs */ +#define AUDIO_CLOCK_SEL_512FS 5L /* 512*fs */ + +/* Page 0x20: EDID and Hotplug Detect */ +#define REG_EDID_IN_BYTE0 0x2000 /* EDID base */ +#define REG_EDID_IN_VERSION 0x2080 +#define REG_EDID_ENABLE 0x2081 +#define REG_HPD_POWER 0x2084 +#define REG_HPD_AUTO_CTRL 0x2085 +#define REG_HPD_DURATION 0x2086 +#define REG_RX_HPD_HEAC 0x2087 + +/* EDID_ENABLE */ +#define EDID_ENABLE_NACK_OFF BIT(7) +#define EDID_ENABLE_EDID_ONLY BIT(6) +#define EDID_ENABLE_B_EN BIT(1) +#define EDID_ENABLE_A_EN BIT(0) + +/* HPD Power */ +#define HPD_POWER_BP_MASK 0x0c +#define HPD_POWER_BP_SHIFT 2 +#define HPD_POWER_BP_LOW 0L +#define HPD_POWER_BP_HIGH 1L +#define HPD_POWER_EDID_ONLY BIT(1) + +/* HPD Auto control */ +#define HPD_AUTO_READ_EDID BIT(7) +#define HPD_AUTO_HPD_F3TECH BIT(5) +#define HPD_AUTO_HP_OTHER BIT(4) +#define HPD_AUTO_HPD_UNSEL BIT(3) +#define HPD_AUTO_HPD_ALL_CH BIT(2) +#define HPD_AUTO_HPD_PRV_CH BIT(1) +#define HPD_AUTO_HPD_NEW_CH BIT(0) + +/* Page 0x21 - EDID content */ +#define REG_EDID_IN_BYTE128 0x2100 /* CEA Extension block */ +#define REG_EDID_IN_SPA_SUB 0x2180 +#define REG_EDID_IN_SPA_AB_A 0x2181 +#define REG_EDID_IN_SPA_CD_A 0x2182 +#define REG_EDID_IN_CKSUM_A 0x2183 +#define REG_EDID_IN_SPA_AB_B 0x2184 +#define REG_EDID_IN_SPA_CD_B 0x2185 +#define REG_EDID_IN_CKSUM_B 0x2186 + +/* Page 0x30 - NV Configuration */ +#define REG_RT_AUTO_CTRL 0x3000 +#define REG_EQ_MAN_CTRL0 0x3001 +#define REG_EQ_MAN_CTRL1 0x3002 +#define REG_OUTPUT_CFG 0x3003 +#define REG_MUTE_CTRL 0x3004 +#define REG_SLAVE_ADDR 0x3005 +#define REG_CMTP_REG6 0x3006 +#define REG_CMTP_REG7 0x3007 +#define REG_CMTP_REG8 0x3008 +#define REG_CMTP_REG9 0x3009 +#define REG_CMTP_REGA 0x300A +#define REG_CMTP_REGB 0x300B +#define REG_CMTP_REGC 0x300C +#define REG_CMTP_REGD 0x300D +#define REG_CMTP_REGE 0x300E +#define REG_CMTP_REGF 0x300F +#define REG_CMTP_REG10 0x3010 +#define REG_CMTP_REG11 0x3011 + +/* Page 0x80 - CEC */ +#define REG_PWR_CONTROL 0x80F4 +#define REG_OSC_DIVIDER 0x80F5 +#define REG_EN_OSC_PERIOD_LSB 0x80F8 +#define REG_CONTROL 0x80FF + +/* global interrupt flags (INT_FLG_CRL_TOP) */ +#define INTERRUPT_AFE BIT(7) /* AFE module */ +#define INTERRUPT_HDCP BIT(6) /* HDCP module */ +#define INTERRUPT_AUDIO BIT(5) /* Audio module */ +#define INTERRUPT_INFO BIT(4) /* Infoframe module */ +#define INTERRUPT_MODE BIT(3) /* HDMI mode module */ +#define INTERRUPT_RATE BIT(2) /* rate module */ +#define INTERRUPT_DDC BIT(1) /* DDC module */ +#define INTERRUPT_SUS BIT(0) /* SUS module */ + +/* INT_FLG_CLR_HDCP bits */ +#define MASK_HDCP_MTP BIT(7) /* HDCP MTP busy */ +#define MASK_HDCP_DLMTP BIT(4) /* HDCP end download MTP to SRAM */ +#define MASK_HDCP_DLRAM BIT(3) /* HDCP end download keys from SRAM */ +#define MASK_HDCP_ENC BIT(2) /* HDCP ENC */ +#define MASK_STATE_C5 BIT(1) /* HDCP State C5 reached */ +#define MASK_AKSV BIT(0) /* AKSV received (start of auth) */ + +/* INT_FLG_CLR_RATE bits */ +#define MASK_RATE_B_DRIFT BIT(7) /* Rate measurement drifted */ +#define MASK_RATE_B_ST BIT(6) /* Rate measurement stability change */ +#define MASK_RATE_B_ACT BIT(5) /* Rate measurement activity change */ +#define MASK_RATE_B_PST BIT(4) /* Rate measreument presence change */ +#define MASK_RATE_A_DRIFT BIT(3) /* Rate measurement drifted */ +#define MASK_RATE_A_ST BIT(2) /* Rate measurement stability change */ +#define MASK_RATE_A_ACT BIT(1) /* Rate measurement presence change */ +#define MASK_RATE_A_PST BIT(0) /* Rate measreument presence change */ + +/* INT_FLG_CLR_SUS (Start Up Sequencer) bits */ +#define MASK_MPT BIT(7) /* Config MTP end of process */ +#define MASK_FMT BIT(5) /* Video format changed */ +#define MASK_RT_PULSE BIT(4) /* End of termination resistance pulse */ +#define MASK_SUS_END BIT(3) /* SUS last state reached */ +#define MASK_SUS_ACT BIT(2) /* Activity of selected input changed */ +#define MASK_SUS_CH BIT(1) /* Selected input changed */ +#define MASK_SUS_ST BIT(0) /* SUS state changed */ + +/* INT_FLG_CLR_DDC bits */ +#define MASK_EDID_MTP BIT(7) /* EDID MTP end of process */ +#define MASK_DDC_ERR BIT(6) /* master DDC error */ +#define MASK_DDC_CMD_DONE BIT(5) /* master DDC cmd send correct */ +#define MASK_READ_DONE BIT(4) /* End of down EDID read */ +#define MASK_RX_DDC_SW BIT(3) /* Output DDC switching finished */ +#define MASK_HDCP_DDC_SW BIT(2) /* HDCP DDC switching finished */ +#define MASK_HDP_PULSE_END BIT(1) /* End of Hot Plug Detect pulse */ +#define MASK_DET_5V BIT(0) /* Detection of +5V */ + +/* INT_FLG_CLR_MODE bits */ +#define MASK_HDMI_FLG BIT(7) /* HDMI mode/avmute/encrypt/FIFO fail */ +#define MASK_GAMUT BIT(6) /* Gamut packet */ +#define MASK_ISRC2 BIT(5) /* ISRC2 packet */ +#define MASK_ISRC1 BIT(4) /* ISRC1 packet */ +#define MASK_ACP BIT(3) /* Audio Content Protection packet */ +#define MASK_DC_NO_GCP BIT(2) /* GCP not received in 5 frames */ +#define MASK_DC_PHASE BIT(1) /* deepcolor pixel phase needs update */ +#define MASK_DC_MODE BIT(0) /* deepcolor color depth changed */ + +/* INT_FLG_CLR_INFO bits (Infoframe Change Status) */ +#define MASK_MPS_IF BIT(6) /* MPEG Source Product */ +#define MASK_AUD_IF BIT(5) /* Audio */ +#define MASK_SPD_IF BIT(4) /* Source Product Descriptor */ +#define MASK_AVI_IF BIT(3) /* Auxiliary Video IF */ +#define MASK_VS_IF_OTHER_BK2 BIT(2) /* Vendor Specific (bank2) */ +#define MASK_VS_IF_OTHER_BK1 BIT(1) /* Vendor Specific (bank1) */ +#define MASK_VS_IF_HDMI BIT(0) /* Vendor Specific (w/ HDMI LLC code) */ + +/* INT_FLG_CLR_AUDIO bits */ +#define MASK_AUDIO_FREQ_FLG BIT(5) /* Audio freq change */ +#define MASK_AUDIO_FLG BIT(4) /* DST, OBA, HBR, ASP change */ +#define MASK_MUTE_FLG BIT(3) /* Audio Mute */ +#define MASK_CH_STATE BIT(2) /* Channel status */ +#define MASK_UNMUTE_FIFO BIT(1) /* Audio Unmute */ +#define MASK_ERROR_FIFO_PT BIT(0) /* Audio FIFO pointer error */ + +/* INT_FLG_CLR_AFE bits */ +#define MASK_AFE_WDL_UNLOCKED BIT(7) /* Wordlocker was unlocked */ +#define MASK_AFE_GAIN_DONE BIT(6) /* Gain calibration done */ +#define MASK_AFE_OFFSET_DONE BIT(5) /* Offset calibration done */ +#define MASK_AFE_ACTIVITY_DET BIT(4) /* Activity detected on data */ +#define MASK_AFE_PLL_LOCK BIT(3) /* TMDS PLL is locked */ +#define MASK_AFE_TRMCAL_DONE BIT(2) /* Termination calibration done */ +#define MASK_AFE_ASU_STATE BIT(1) /* ASU state is reached */ +#define MASK_AFE_ASU_READY BIT(0) /* AFE calibration done: TMDS ready */ + +/* Audio Output */ +#define AUDCFG_CLK_INVERT BIT(7) /* invert A_CLK polarity */ +#define AUDCFG_TEST_TONE BIT(6) /* enable test tone generator */ +#define AUDCFG_BUS_SHIFT 5 +#define AUDCFG_BUS_I2S 0L +#define AUDCFG_BUS_SPDIF 1L +#define AUDCFG_I2SW_SHIFT 4 +#define AUDCFG_I2SW_16 0L +#define AUDCFG_I2SW_32 1L +#define AUDCFG_AUTO_MUTE_EN BIT(3) /* Enable Automatic audio mute */ +#define AUDCFG_HBR_SHIFT 2 +#define AUDCFG_HBR_STRAIGHT 0L /* straight via AP0 */ +#define AUDCFG_HBR_DEMUX 1L /* demuxed via AP0:AP3 */ +#define AUDCFG_TYPE_MASK 0x03 +#define AUDCFG_TYPE_SHIFT 0 +#define AUDCFG_TYPE_DST 3L /* Direct Stream Transfer (DST) */ +#define AUDCFG_TYPE_OBA 2L /* One Bit Audio (OBA) */ +#define AUDCFG_TYPE_HBR 1L /* High Bit Rate (HBR) */ +#define AUDCFG_TYPE_PCM 0L /* Audio samples */ + +/* Video Formatter */ +#define OF_VP_ENABLE BIT(7) /* VP[35:0]/HS/VS/DE/CLK */ +#define OF_BLK BIT(4) /* blanking codes */ +#define OF_TRC BIT(3) /* timing codes (SAV/EAV) */ +#define OF_FMT_MASK 0x3 +#define OF_FMT_444 0L /* RGB444/YUV444 */ +#define OF_FMT_422_SMPT 1L /* YUV422 semi-planar */ +#define OF_FMT_422_CCIR 2L /* YUV422 CCIR656 */ + +/* HS/HREF output control */ +#define HS_HREF_DELAY_MASK 0xf0 +#define HS_HREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */ +#define HS_HREF_PXQ_SHIFT 3 /* Timing codes from HREF */ +#define HS_HREF_INV_SHIFT 2 /* polarity (1=invert) */ +#define HS_HREF_SEL_MASK 0x03 +#define HS_HREF_SEL_SHIFT 0 +#define HS_HREF_SEL_HS_VHREF 0L /* HS from VHREF */ +#define HS_HREF_SEL_HREF_VHREF 1L /* HREF from VHREF */ +#define HS_HREF_SEL_HREF_HDMI 2L /* HREF from HDMI */ +#define HS_HREF_SEL_NONE 3L /* not generated */ + +/* VS output control */ +#define VS_VREF_DELAY_MASK 0xf0 +#define VS_VREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */ +#define VS_VREF_INV_SHIFT 2 /* polarity (1=invert) */ +#define VS_VREF_SEL_MASK 0x03 +#define VS_VREF_SEL_SHIFT 0 +#define VS_VREF_SEL_VS_VHREF 0L /* VS from VHREF */ +#define VS_VREF_SEL_VREF_VHREF 1L /* VREF from VHREF */ +#define VS_VREF_SEL_VREF_HDMI 2L /* VREF from HDMI */ +#define VS_VREF_SEL_NONE 3L /* not generated */ + +/* DE/FREF output control */ +#define DE_FREF_DELAY_MASK 0xf0 +#define DE_FREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */ +#define DE_FREF_DE_PXQ_SHIFT 3 /* Timing codes from DE */ +#define DE_FREF_INV_SHIFT 2 /* polarity (1=invert) */ +#define DE_FREF_SEL_MASK 0x03 +#define DE_FREF_SEL_SHIFT 0 +#define DE_FREF_SEL_DE_VHREF 0L /* DE from VHREF (HREF and not(VREF) */ +#define DE_FREF_SEL_FREF_VHREF 1L /* FREF from VHREF */ +#define DE_FREF_SEL_FREF_HDMI 2L /* FREF from HDMI */ +#define DE_FREF_SEL_NONE 3L /* not generated */ + +/* HDMI_SOFT_RST bits */ +#define RESET_DC BIT(7) /* Reset deep color module */ +#define RESET_HDCP BIT(6) /* Reset HDCP module */ +#define RESET_KSV BIT(5) /* Reset KSV-FIFO */ +#define RESET_SCFG BIT(4) /* Reset HDCP and repeater function */ +#define RESET_HCFG BIT(3) /* Reset HDCP DDC part */ +#define RESET_PA BIT(2) /* Reset polarity adjust */ +#define RESET_EP BIT(1) /* Reset Error protection */ +#define RESET_TMDS BIT(0) /* Reset TMDS (calib, encoding, flow) */ + +/* HDMI_INFO_RST bits */ +#define NACK_HDCP BIT(7) /* No ACK on HDCP request */ +#define RESET_FIFO BIT(4) /* Reset Audio FIFO control */ +#define RESET_GAMUT BIT(3) /* Clear Gamut packet */ +#define RESET_AI BIT(2) /* Clear ACP and ISRC packets */ +#define RESET_IF BIT(1) /* Clear all Audio infoframe packets */ +#define RESET_AUDIO BIT(0) /* Reset Audio FIFO control */ + +/* HDCP_BCAPS bits */ +#define HDCP_HDMI BIT(7) /* HDCP suports HDMI (vs DVI only) */ +#define HDCP_REPEATER BIT(6) /* HDCP supports repeater function */ +#define HDCP_READY BIT(5) /* set by repeater function */ +#define HDCP_FAST BIT(4) /* Up to 400kHz */ +#define HDCP_11 BIT(1) /* HDCP 1.1 supported */ +#define HDCP_FAST_REAUTH BIT(0) /* fast reauthentication supported */ + +/* Audio output formatter */ +#define AUDIO_LAYOUT_SP_FLAG BIT(2) /* sp flag used by FIFO */ +#define AUDIO_LAYOUT_MANUAL BIT(1) /* manual layout (vs per pkt) */ +#define AUDIO_LAYOUT_LAYOUT1 BIT(0) /* Layout1: AP0-3 vs Layout0:AP0 */ + +/* masks for interrupt status registers */ +#define MASK_SUS_STATUS 0x1F +#define LAST_STATE_REACHED 0x1B +#define MASK_CLK_STABLE 0x04 +#define MASK_CLK_ACTIVE 0x02 +#define MASK_SUS_STATE 0x10 +#define MASK_SR_FIFO_FIFO_CTRL 0x30 +#define MASK_AUDIO_FLAG 0x10 + +/* Rate measurement */ +#define RATE_REFTIM_ENABLE 0x01 +#define CLK_MIN_RATE 0x0057e4 +#define CLK_MAX_RATE 0x0395f8 +#define WDL_CFG_VAL 0x82 +#define DC_FILTER_VAL 0x31 + +/* Infoframe */ +#define VS_HDMI_IF_UPDATE 0x0200 +#define VS_HDMI_IF 0x0201 +#define VS_BK1_IF_UPDATE 0x0220 +#define VS_BK1_IF 0x0221 +#define VS_BK2_IF_UPDATE 0x0240 +#define VS_BK2_IF 0x0241 +#define AVI_IF_UPDATE 0x0260 +#define AVI_IF 0x0261 +#define SPD_IF_UPDATE 0x0280 +#define SPD_IF 0x0281 +#define AUD_IF_UPDATE 0x02a0 +#define AUD_IF 0x02a1 +#define MPS_IF_UPDATE 0x02c0 +#define MPS_IF 0x02c1 diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index f31e659588ac..0dd6ff3e6201 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -68,11 +68,15 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) static int tda9840_status(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); + int rc; u8 byte; - if (1 != i2c_master_recv(client, &byte, 1)) { + rc = i2c_master_recv(client, &byte, 1); + if (rc != 1) { v4l2_dbg(1, debug, sd, "i2c_master_recv() failed\n"); + if (rc < 0) + return rc; return -EIO; } diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 772164b848ef..5919214a56bf 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -156,14 +156,18 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) struct v4l2_subdev *sd = &chip->sd; struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[2]; + int rc; if (subaddr < 0) { v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); chip->shadow.bytes[1] = val; buffer[0] = val; - if (1 != i2c_master_send(c, buffer, 1)) { + rc = i2c_master_send(c, buffer, 1); + if (rc != 1) { v4l2_warn(sd, "I/O error (write 0x%x)\n", val); - return -1; + if (rc < 0) + return rc; + return -EIO; } } else { if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { @@ -178,10 +182,13 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) chip->shadow.bytes[subaddr+1] = val; buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(c, buffer, 2)) { + rc = i2c_master_send(c, buffer, 2); + if (rc != 2) { v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", subaddr, val); - return -1; + if (rc < 0) + return rc; + return -EIO; } } return 0; @@ -214,10 +221,14 @@ static int chip_read(struct CHIPSTATE *chip) struct v4l2_subdev *sd = &chip->sd; struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer; + int rc; - if (1 != i2c_master_recv(c, &buffer, 1)) { + rc = i2c_master_recv(c, &buffer, 1); + if (rc != 1) { v4l2_warn(sd, "I/O error (read)\n"); - return -1; + if (rc < 0) + return rc; + return -EIO; } v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); return buffer; @@ -227,6 +238,7 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr) { struct v4l2_subdev *sd = &chip->sd; struct i2c_client *c = v4l2_get_subdevdata(sd); + int rc; unsigned char write[1]; unsigned char read[1]; struct i2c_msg msgs[2] = { @@ -245,9 +257,12 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr) write[0] = subaddr; - if (2 != i2c_transfer(c->adapter, msgs, 2)) { + rc = i2c_transfer(c->adapter, msgs, 2); + if (rc != 2) { v4l2_warn(sd, "I/O error (read2)\n"); - return -1; + if (rc < 0) + return rc; + return -EIO; } v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", subaddr, read[0]); @@ -258,7 +273,7 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) { struct v4l2_subdev *sd = &chip->sd; struct i2c_client *c = v4l2_get_subdevdata(sd); - int i; + int i, rc; if (0 == cmd->count) return 0; @@ -284,9 +299,12 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) printk(KERN_CONT "\n"); /* send data to the chip */ - if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { + rc = i2c_master_send(c, cmd->bytes, cmd->count); + if (rc != cmd->count) { v4l2_warn(sd, "I/O error (%s)\n", name); - return -1; + if (rc < 0) + return rc; + return -EIO; } return 0; } @@ -400,8 +418,12 @@ static int tda9840_getrxsubchans(struct CHIPSTATE *chip) struct v4l2_subdev *sd = &chip->sd; int val, mode; - val = chip_read(chip); mode = V4L2_TUNER_SUB_MONO; + + val = chip_read(chip); + if (val < 0) + return mode; + if (val & TDA9840_DS_DUAL) mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; if (val & TDA9840_ST_STEREO) @@ -445,7 +467,12 @@ static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) static int tda9840_checkit(struct CHIPSTATE *chip) { int rc; + rc = chip_read(chip); + if (rc < 0) + return 0; + + /* lower 5 bits should be 0 */ return ((rc & 0x1f) == 0) ? 1 : 0; } @@ -563,6 +590,9 @@ static int tda985x_getrxsubchans(struct CHIPSTATE *chip) /* Allows forced mono */ mode = V4L2_TUNER_SUB_MONO; val = chip_read(chip); + if (val < 0) + return mode; + if (val & TDA985x_STP) mode = V4L2_TUNER_SUB_STEREO; if (val & TDA985x_SAPP) @@ -720,8 +750,12 @@ static int tda9873_getrxsubchans(struct CHIPSTATE *chip) struct v4l2_subdev *sd = &chip->sd; int val,mode; - val = chip_read(chip); mode = V4L2_TUNER_SUB_MONO; + + val = chip_read(chip); + if (val < 0) + return mode; + if (val & TDA9873_STEREO) mode = V4L2_TUNER_SUB_STEREO; if (val & TDA9873_DUAL) @@ -780,7 +814,8 @@ static int tda9873_checkit(struct CHIPSTATE *chip) { int rc; - if (-1 == (rc = chip_read2(chip,254))) + rc = chip_read2(chip, 254); + if (rc < 0) return 0; return (rc & ~0x1f) == 0x80; } @@ -926,11 +961,14 @@ static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) mode = V4L2_TUNER_SUB_MONO; - if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) + dsr = chip_read2(chip, TDA9874A_DSR); + if (dsr < 0) return mode; - if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) + nsr = chip_read2(chip, TDA9874A_NSR); + if (nsr < 0) return mode; - if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) + necr = chip_read2(chip, TDA9874A_NECR); + if (necr < 0) return mode; /* need to store dsr/nsr somewhere */ @@ -1059,9 +1097,11 @@ static int tda9874a_checkit(struct CHIPSTATE *chip) struct v4l2_subdev *sd = &chip->sd; int dic,sic; /* device id. and software id. codes */ - if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) + dic = chip_read2(chip, TDA9874A_DIC); + if (dic < 0) return 0; - if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) + sic = chip_read2(chip, TDA9874A_SIC); + if (sic < 0) return 0; v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); @@ -1201,7 +1241,11 @@ static int tda9875_checkit(struct CHIPSTATE *chip) int dic, rev; dic = chip_read2(chip, 254); + if (dic < 0) + return 0; rev = chip_read2(chip, 255); + if (rev < 0) + return 0; if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ v4l2_info(sd, "found tda9875%s rev. %d.\n", @@ -1377,8 +1421,12 @@ static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) { int val, mode; - val = chip_read(chip); mode = V4L2_TUNER_SUB_MONO; + + val = chip_read(chip); + if (val < 0) + return mode; + if (val & TA8874Z_B1){ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; }else if (!(val & TA8874Z_B0)){ @@ -1431,7 +1479,11 @@ static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) static int ta8874z_checkit(struct CHIPSTATE *chip) { int rc; + rc = chip_read(chip); + if (rc < 0) + return rc; + return ((rc & 0x1f) == 0x1f) ? 1 : 0; } diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 8b0aa9297bde..6a9890531d01 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -747,60 +747,47 @@ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) } /** - * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm + * tvp514x_g_frame_interval() - V4L2 decoder interface handler * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * @ival: pointer to a v4l2_subdev_frame_interval structure * * Returns the decoder's video CAPTURE parameters. */ static int -tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +tvp514x_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct tvp514x_decoder *decoder = to_decoder(sd); - struct v4l2_captureparm *cparm; enum tvp514x_std current_std; - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; /* get the current standard */ current_std = decoder->current_std; - cparm = &a->parm.capture; - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = + ival->interval = decoder->std_list[current_std].standard.frameperiod; return 0; } /** - * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm + * tvp514x_s_frame_interval() - V4L2 decoder interface handler * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * @ival: pointer to a v4l2_subdev_frame_interval structure * * Configures the decoder to use the input parameters, if possible. If * not possible, returns the appropriate error code. */ static int -tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +tvp514x_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_fract *timeperframe; enum tvp514x_std current_std; - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; - timeperframe = &a->parm.capture.timeperframe; + timeperframe = &ival->interval; /* get the current standard */ current_std = decoder->current_std; @@ -961,8 +948,8 @@ static const struct v4l2_subdev_video_ops tvp514x_video_ops = { .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, .querystd = tvp514x_querystd, - .g_parm = tvp514x_g_parm, - .s_parm = tvp514x_s_parm, + .g_frame_interval = tvp514x_g_frame_interval, + .s_frame_interval = tvp514x_s_frame_interval, .s_stream = tvp514x_s_stream, }; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 3c1851984b90..2476d812f669 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -505,80 +505,77 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = /* FIXME: Current api doesn't handle all VBI types, those not yet supported are placed under #if 0 */ #if 0 - {0x010, /* Teletext, SECAM, WST System A */ + [0] = {0x010, /* Teletext, SECAM, WST System A */ {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #endif - {0x030, /* Teletext, PAL, WST System B */ + [1] = {0x030, /* Teletext, PAL, WST System B */ {V4L2_SLICED_TELETEXT_B,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #if 0 - {0x050, /* Teletext, PAL, WST System C */ + [2] = {0x050, /* Teletext, PAL, WST System C */ {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x070, /* Teletext, NTSC, WST System B */ + [3] = {0x070, /* Teletext, NTSC, WST System B */ {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x090, /* Tetetext, NTSC NABTS System C */ + [4] = {0x090, /* Tetetext, NTSC NABTS System C */ {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, - {0x0b0, /* Teletext, NTSC-J, NABTS System D */ + [5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */ {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x0d0, /* Closed Caption, PAL/SECAM */ + [6] = {0x0d0, /* Closed Caption, PAL/SECAM */ {V4L2_SLICED_CAPTION_625,22,22,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, #endif - {0x0f0, /* Closed Caption, NTSC */ + [7] = {0x0f0, /* Closed Caption, NTSC */ {V4L2_SLICED_CAPTION_525,21,21,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, - {0x110, /* Wide Screen Signal, PAL/SECAM */ + [8] = {0x110, /* Wide Screen Signal, PAL/SECAM */ {V4L2_SLICED_WSS_625,23,23,1}, { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, #if 0 - {0x130, /* Wide Screen Signal, NTSC C */ + [9] = {0x130, /* Wide Screen Signal, NTSC C */ {V4L2_SLICED_WSS_525,20,20,1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, - {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ + [10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ {V4l2_SLICED_VITC_625,6,22,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, - {0x170, /* Vertical Interval Timecode (VITC), NTSC */ + [11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */ {V4l2_SLICED_VITC_525,10,20,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, #endif - {0x190, /* Video Program System (VPS), PAL */ + [12] = {0x190, /* Video Program System (VPS), PAL */ {V4L2_SLICED_VPS,16,16,0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } }, /* 0x1d0 User programmable */ - - /* End of struct */ - { (u16)-1 } }; static int tvp5150_write_inittab(struct v4l2_subdev *sd, @@ -591,10 +588,10 @@ static int tvp5150_write_inittab(struct v4l2_subdev *sd, return 0; } -static int tvp5150_vdp_init(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs) +static int tvp5150_vdp_init(struct v4l2_subdev *sd) { unsigned int i; + int j; /* Disable Full Field */ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); @@ -604,14 +601,17 @@ static int tvp5150_vdp_init(struct v4l2_subdev *sd, tvp5150_write(sd, i, 0xff); /* Load Ram Table */ - while (regs->reg != (u16)-1) { + for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[j]; + + if (!regs->type.vbi_type) + continue; + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); for (i = 0; i < 16; i++) tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); - - regs++; } return 0; } @@ -620,19 +620,23 @@ static int tvp5150_vdp_init(struct v4l2_subdev *sd, static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap) { - const struct i2c_vbi_ram_value *regs = vbi_ram_default; - int line; + int line, i; dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n"); memset(cap, 0, sizeof *cap); - while (regs->reg != (u16)-1 ) { - for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { + for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; + + if (!regs->type.vbi_type) + continue; + + for (line = regs->type.ini_line; + line <= regs->type.end_line; + line++) { cap->service_lines[0][line] |= regs->type.vbi_type; } cap->service_set |= regs->type.vbi_type; - - regs++; } return 0; } @@ -651,14 +655,13 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, * MSB = field2 */ static int tvp5150_set_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, unsigned int type,u8 flags, int line, const int fields) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std = decoder->norm; u8 reg; - int pos = 0; + int i, pos = 0; if (std == V4L2_STD_ALL) { dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n"); @@ -671,19 +674,19 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd, if (line < 6 || line > 27) return 0; - while (regs->reg != (u16)-1) { + for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; + + if (!regs->type.vbi_type) + continue; + if ((type & regs->type.vbi_type) && (line >= regs->type.ini_line) && (line <= regs->type.end_line)) break; - - regs++; pos++; } - if (regs->reg == (u16)-1) - return 0; - type = pos | (flags & 0xf0); reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; @@ -696,8 +699,7 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd, return type; } -static int tvp5150_get_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, int line) +static int tvp5150_get_vbi(struct v4l2_subdev *sd, int line) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std = decoder->norm; @@ -726,8 +728,8 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd, return 0; } pos = ret & 0x0f; - if (pos < 0x0f) - type |= regs[pos].type.vbi_type; + if (pos < ARRAY_SIZE(vbi_ram_default)) + type |= vbi_ram_default[pos].type.vbi_type; } return type; @@ -788,7 +790,7 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) tvp5150_write_inittab(sd, tvp5150_init_default); /* Initializes VDP registers */ - tvp5150_vdp_init(sd, vbi_ram_default); + tvp5150_vdp_init(sd); /* Selects decoder input */ tvp5150_selmux(sd); @@ -1121,8 +1123,8 @@ static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f for (i = 0; i <= 23; i++) { svbi->service_lines[1][i] = 0; svbi->service_lines[0][i] = - tvp5150_set_vbi(sd, vbi_ram_default, - svbi->service_lines[0][i], 0xf0, i, 3); + tvp5150_set_vbi(sd, svbi->service_lines[0][i], + 0xf0, i, 3); } /* Enables FIFO */ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); @@ -1148,7 +1150,7 @@ static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f for (i = 0; i <= 23; i++) { svbi->service_lines[0][i] = - tvp5150_get_vbi(sd, vbi_ram_default, i); + tvp5150_get_vbi(sd, i); mask |= svbi->service_lines[0][i]; } svbi->service_set = mask; diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c new file mode 100644 index 000000000000..a54548cc4285 --- /dev/null +++ b/drivers/media/i2c/tw9910.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * tw9910 Video Driver + * + * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov772x driver, + * + * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com> + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> + +#include <media/i2c/tw9910.h> +#include <media/v4l2-subdev.h> + +#define GET_ID(val) ((val & 0xF8) >> 3) +#define GET_REV(val) (val & 0x07) + +/* + * register offset + */ +#define ID 0x00 /* Product ID Code Register */ +#define STATUS1 0x01 /* Chip Status Register I */ +#define INFORM 0x02 /* Input Format */ +#define OPFORM 0x03 /* Output Format Control Register */ +#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ +#define OUTCTR1 0x05 /* Output Control I */ +#define ACNTL1 0x06 /* Analog Control Register 1 */ +#define CROP_HI 0x07 /* Cropping Register, High */ +#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ +#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ +#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ +#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ +#define CNTRL1 0x0C /* Control Register I */ +#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ +#define SCALE_HI 0x0E /* Scaling Register, High */ +#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ +#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ +#define CONTRAST 0x11 /* CONTRAST Control Register */ +#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ +#define SAT_U 0x13 /* Chroma (U) Gain Register */ +#define SAT_V 0x14 /* Chroma (V) Gain Register */ +#define HUE 0x15 /* Hue Control Register */ +#define CORING1 0x17 +#define CORING2 0x18 /* Coring and IF compensation */ +#define VBICNTL 0x19 /* VBI Control Register */ +#define ACNTL2 0x1A /* Analog Control 2 */ +#define OUTCTR2 0x1B /* Output Control 2 */ +#define SDT 0x1C /* Standard Selection */ +#define SDTR 0x1D /* Standard Recognition */ +#define TEST 0x1F /* Test Control Register */ +#define CLMPG 0x20 /* Clamping Gain */ +#define IAGC 0x21 /* Individual AGC Gain */ +#define AGCGAIN 0x22 /* AGC Gain */ +#define PEAKWT 0x23 /* White Peak Threshold */ +#define CLMPL 0x24 /* Clamp level */ +#define SYNCT 0x25 /* Sync Amplitude */ +#define MISSCNT 0x26 /* Sync Miss Count Register */ +#define PCLAMP 0x27 /* Clamp Position Register */ +#define VCNTL1 0x28 /* Vertical Control I */ +#define VCNTL2 0x29 /* Vertical Control II */ +#define CKILL 0x2A /* Color Killer Level Control */ +#define COMB 0x2B /* Comb Filter Control */ +#define LDLY 0x2C /* Luma Delay and H Filter Control */ +#define MISC1 0x2D /* Miscellaneous Control I */ +#define LOOP 0x2E /* LOOP Control Register */ +#define MISC2 0x2F /* Miscellaneous Control II */ +#define MVSN 0x30 /* Macrovision Detection */ +#define STATUS2 0x31 /* Chip STATUS II */ +#define HFREF 0x32 /* H monitor */ +#define CLMD 0x33 /* CLAMP MODE */ +#define IDCNTL 0x34 /* ID Detection Control */ +#define CLCNTL1 0x35 /* Clamp Control I */ +#define ANAPLLCTL 0x4C +#define VBIMIN 0x4D +#define HSLOWCTL 0x4E +#define WSS3 0x4F +#define FILLDATA 0x50 +#define SDID 0x51 +#define DID 0x52 +#define WSS1 0x53 +#define WSS2 0x54 +#define VVBI 0x55 +#define LCTL6 0x56 +#define LCTL7 0x57 +#define LCTL8 0x58 +#define LCTL9 0x59 +#define LCTL10 0x5A +#define LCTL11 0x5B +#define LCTL12 0x5C +#define LCTL13 0x5D +#define LCTL14 0x5E +#define LCTL15 0x5F +#define LCTL16 0x60 +#define LCTL17 0x61 +#define LCTL18 0x62 +#define LCTL19 0x63 +#define LCTL20 0x64 +#define LCTL21 0x65 +#define LCTL22 0x66 +#define LCTL23 0x67 +#define LCTL24 0x68 +#define LCTL25 0x69 +#define LCTL26 0x6A +#define HSBEGIN 0x6B +#define HSEND 0x6C +#define OVSDLY 0x6D +#define OVSEND 0x6E +#define VBIDELAY 0x6F + +/* + * register detail + */ + +/* INFORM */ +#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ +#define FC27_FF 0x00 /* 0 : Square pixel mode. */ + /* Must use 24.54MHz for 60Hz field rate */ + /* source or 29.5MHz for 50Hz field rate */ +#define IFSEL_S 0x10 /* 01 : S-video decoding */ +#define IFSEL_C 0x00 /* 00 : Composite video decoding */ + /* Y input video selection */ +#define YSEL_M0 0x00 /* 00 : Mux0 selected */ +#define YSEL_M1 0x04 /* 01 : Mux1 selected */ +#define YSEL_M2 0x08 /* 10 : Mux2 selected */ +#define YSEL_M3 0x10 /* 11 : Mux3 selected */ + +/* OPFORM */ +#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ + /* 1 : ITU-R-656 compatible data sequence format */ +#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ + /* 1 : 16-bit YCrCb 4:2:2 output format.*/ +#define LLCMODE 0x20 /* 1 : LLC output mode. */ + /* 0 : free-run output mode */ +#define AINC 0x10 /* Serial interface auto-indexing control */ + /* 0 : auto-increment */ + /* 1 : non-auto */ +#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ + /* 0 : Vertical out ctrl by HACTIVE and DVALID */ +#define OEN_TRI_SEL_MASK 0x07 +#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */ +#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */ +#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */ + +/* OUTCTR1 */ +#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ +#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ + /* VS pin output control */ +#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ +#define VSSL_VACT 0x10 /* 1 : VACT */ +#define VSSL_FIELD 0x20 /* 2 : FIELD */ +#define VSSL_VVALID 0x30 /* 3 : VVALID */ +#define VSSL_ZERO 0x70 /* 7 : 0 */ +#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ +#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ + /* HS pin output control */ +#define HSSL_HACT 0x00 /* 0 : HACT */ +#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ +#define HSSL_DVALID 0x02 /* 2 : DVALID */ +#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ +#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ +#define HSSL_ZERO 0x07 /* 7 : 0 */ + +/* ACNTL1 */ +#define SRESET 0x80 /* resets the device to its default state + * but all register content remain unchanged. + * This bit is self-resetting. + */ +#define ACNTL1_PDN_MASK 0x0e +#define CLK_PDN 0x08 /* system clock power down */ +#define Y_PDN 0x04 /* Luma ADC power down */ +#define C_PDN 0x02 /* Chroma ADC power down */ + +/* ACNTL2 */ +#define ACNTL2_PDN_MASK 0x40 +#define PLL_PDN 0x40 /* PLL power down */ + +/* VBICNTL */ + +/* RTSEL : control the real time signal output from the MPOUT pin */ +#define RTSEL_MASK 0x07 +#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ +#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ +#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ +#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ +#define RTSEL_MONO 0x04 /* 0100 = MONO */ +#define RTSEL_DET50 0x05 /* 0101 = DET50 */ +#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ +#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ + +/* HSYNC start and end are constant for now */ +#define HSYNC_START 0x0260 +#define HSYNC_END 0x0300 + +/* + * structure + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +struct tw9910_scale_ctrl { + char *name; + unsigned short width; + unsigned short height; + u16 hscale; + u16 vscale; +}; + +struct tw9910_priv { + struct v4l2_subdev subdev; + struct clk *clk; + struct tw9910_video_info *info; + struct gpio_desc *pdn_gpio; + struct gpio_desc *rstb_gpio; + const struct tw9910_scale_ctrl *scale; + v4l2_std_id norm; + u32 revision; +}; + +static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { + { + .name = "NTSC SQ", + .width = 640, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC CCIR601", + .width = 720, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC SQ (CIF)", + .width = 320, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC CCIR601 (CIF)", + .width = 360, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC SQ (QCIF)", + .width = 160, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "NTSC CCIR601 (QCIF)", + .width = 180, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { + { + .name = "PAL SQ", + .width = 768, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL CCIR601", + .width = 720, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL SQ (CIF)", + .width = 384, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL CCIR601 (CIF)", + .width = 360, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL SQ (QCIF)", + .width = 192, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "PAL CCIR601 (QCIF)", + .width = 180, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +/* + * general function + */ +static struct tw9910_priv *to_tw9910(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct tw9910_priv, + subdev); +} + +static int tw9910_mask_set(struct i2c_client *client, u8 command, + u8 mask, u8 set) +{ + s32 val = i2c_smbus_read_byte_data(client, command); + + if (val < 0) + return val; + + val &= ~mask; + val |= set & mask; + + return i2c_smbus_write_byte_data(client, command, val); +} + +static int tw9910_set_scale(struct i2c_client *client, + const struct tw9910_scale_ctrl *scale) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, SCALE_HI, + (scale->vscale & 0x0F00) >> 4 | + (scale->hscale & 0x0F00) >> 8); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HSCALE_LO, + scale->hscale & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VSCALE_LO, + scale->vscale & 0x00FF); + + return ret; +} + +static int tw9910_set_hsync(struct i2c_client *client) +{ + struct tw9910_priv *priv = to_tw9910(client); + int ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSBEGIN, + (HSYNC_START & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSEND, + (HSYNC_END & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* So far only revisions 0 and 1 have been seen. */ + /* bit 2 - 0 */ + if (priv->revision == 1) + ret = tw9910_mask_set(client, HSLOWCTL, 0x77, + (HSYNC_START & 0x0007) << 4 | + (HSYNC_END & 0x0007)); + + return ret; +} + +static void tw9910_reset(struct i2c_client *client) +{ + tw9910_mask_set(client, ACNTL1, SRESET, SRESET); + usleep_range(1000, 5000); +} + +static int tw9910_power(struct i2c_client *client, int enable) +{ + int ret; + u8 acntl1; + u8 acntl2; + + if (enable) { + acntl1 = 0; + acntl2 = 0; + } else { + acntl1 = CLK_PDN | Y_PDN | C_PDN; + acntl2 = PLL_PDN; + } + + ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1); + if (ret < 0) + return ret; + + return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); +} + +static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, + u32 width, u32 height) +{ + const struct tw9910_scale_ctrl *scale; + const struct tw9910_scale_ctrl *ret = NULL; + __u32 diff = 0xffffffff, tmp; + int size, i; + + if (norm & V4L2_STD_NTSC) { + scale = tw9910_ntsc_scales; + size = ARRAY_SIZE(tw9910_ntsc_scales); + } else if (norm & V4L2_STD_PAL) { + scale = tw9910_pal_scales; + size = ARRAY_SIZE(tw9910_pal_scales); + } else { + return NULL; + } + + for (i = 0; i < size; i++) { + tmp = abs(width - scale[i].width) + + abs(height - scale[i].height); + if (tmp < diff) { + diff = tmp; + ret = scale + i; + } + } + + return ret; +} + +/* + * subdevice operations + */ +static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + u8 val; + int ret; + + if (!enable) { + switch (priv->revision) { + case 0: + val = OEN_TRI_SEL_ALL_OFF_r0; + break; + case 1: + val = OEN_TRI_SEL_ALL_OFF_r1; + break; + default: + dev_err(&client->dev, "un-supported revision\n"); + return -EINVAL; + } + } else { + val = OEN_TRI_SEL_ALL_ON; + + if (!priv->scale) { + dev_err(&client->dev, "norm select error\n"); + return -EPERM; + } + + dev_dbg(&client->dev, "%s %dx%d\n", + priv->scale->name, + priv->scale->width, + priv->scale->height); + } + + ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val); + if (ret < 0) + return ret; + + return tw9910_power(client, enable); +} + +static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + + *norm = priv->norm; + + return 0; +} + +static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + const unsigned int hact = 720; + const unsigned int hdelay = 15; + unsigned int vact; + unsigned int vdelay; + int ret; + + if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) + return -EINVAL; + + priv->norm = norm; + if (norm & V4L2_STD_525_60) { + vact = 240; + vdelay = 18; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); + } else { + vact = 288; + vdelay = 24; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); + } + if (!ret) + ret = i2c_smbus_write_byte_data(client, CROP_HI, + ((vdelay >> 2) & 0xc0) | + ((vact >> 4) & 0x30) | + ((hdelay >> 6) & 0x0c) | + ((hact >> 8) & 0x03)); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VDELAY_LO, + vdelay & 0xff); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, + vact & 0xff); + + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int tw9910_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 1; + ret = i2c_smbus_read_byte_data(client, reg->reg); + if (ret < 0) + return ret; + + /* + * ret = int + * reg->val = __u64 + */ + reg->val = (__u64)ret; + + return 0; +} + +static int tw9910_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); +} +#endif + +static int tw9910_power_on(struct tw9910_priv *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + int ret; + + if (priv->clk) { + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + } + + if (priv->pdn_gpio) { + gpiod_set_value(priv->pdn_gpio, 0); + usleep_range(500, 1000); + } + + /* + * FIXME: The reset signal is connected to a shared GPIO on some + * platforms (namely the SuperH Migo-R). Until a framework becomes + * available to handle this cleanly, request the GPIO temporarily + * to avoid conflicts. + */ + priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb", + GPIOD_OUT_LOW); + if (IS_ERR(priv->rstb_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + return PTR_ERR(priv->rstb_gpio); + } + + if (priv->rstb_gpio) { + gpiod_set_value(priv->rstb_gpio, 1); + usleep_range(500, 1000); + gpiod_set_value(priv->rstb_gpio, 0); + usleep_range(500, 1000); + + gpiod_put(priv->rstb_gpio); + } + + return 0; +} + +static int tw9910_power_off(struct tw9910_priv *priv) +{ + clk_disable_unprepare(priv->clk); + + if (priv->pdn_gpio) { + gpiod_set_value(priv->pdn_gpio, 1); + usleep_range(500, 1000); + } + + return 0; +} + +static int tw9910_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + + return on ? tw9910_power_on(priv) : tw9910_power_off(priv); +} + +static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + int ret = -EINVAL; + u8 val; + + /* Select suitable norm. */ + priv->scale = tw9910_select_norm(priv->norm, *width, *height); + if (!priv->scale) + goto tw9910_set_fmt_error; + + /* Reset hardware. */ + tw9910_reset(client); + + /* Set bus width. */ + val = 0x00; + if (priv->info->buswidth == 16) + val = LEN; + + ret = tw9910_mask_set(client, OPFORM, LEN, val); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* Select MPOUT behavior. */ + switch (priv->info->mpout) { + case TW9910_MPO_VLOSS: + val = RTSEL_VLOSS; break; + case TW9910_MPO_HLOCK: + val = RTSEL_HLOCK; break; + case TW9910_MPO_SLOCK: + val = RTSEL_SLOCK; break; + case TW9910_MPO_VLOCK: + val = RTSEL_VLOCK; break; + case TW9910_MPO_MONO: + val = RTSEL_MONO; break; + case TW9910_MPO_DET50: + val = RTSEL_DET50; break; + case TW9910_MPO_FIELD: + val = RTSEL_FIELD; break; + case TW9910_MPO_RTCO: + val = RTSEL_RTCO; break; + default: + val = 0; + } + + ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* Set scale. */ + ret = tw9910_set_scale(client, priv->scale); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* Set hsync. */ + ret = tw9910_set_hsync(client); + if (ret < 0) + goto tw9910_set_fmt_error; + + *width = priv->scale->width; + *height = priv->scale->height; + + return ret; + +tw9910_set_fmt_error: + + tw9910_reset(client); + priv->scale = NULL; + + return ret; +} + +static int tw9910_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported. */ + if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + if (priv->norm & V4L2_STD_NTSC) { + sel->r.width = 640; + sel->r.height = 480; + } else { + sel->r.width = 768; + sel->r.height = 576; + } + + return 0; +} + +static int tw9910_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + + if (format->pad) + return -EINVAL; + + if (!priv->scale) { + priv->scale = tw9910_select_norm(priv->norm, 640, 480); + if (!priv->scale) + return -EINVAL; + } + + mf->width = priv->scale->width; + mf->height = priv->scale->height; + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->field = V4L2_FIELD_INTERLACED_BT; + + return 0; +} + +static int tw9910_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u32 width = mf->width, height = mf->height; + int ret; + + WARN_ON(mf->field != V4L2_FIELD_ANY && + mf->field != V4L2_FIELD_INTERLACED_BT); + + /* Check color format. */ + if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) + return -EINVAL; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + ret = tw9910_set_frame(sd, &width, &height); + if (ret) + return ret; + + mf->width = width; + mf->height = height; + + return 0; +} + +static int tw9910_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + const struct tw9910_scale_ctrl *scale; + + if (format->pad) + return -EINVAL; + + if (mf->field == V4L2_FIELD_ANY) { + mf->field = V4L2_FIELD_INTERLACED_BT; + } else if (mf->field != V4L2_FIELD_INTERLACED_BT) { + dev_err(&client->dev, "Field type %d invalid\n", mf->field); + return -EINVAL; + } + + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + /* Select suitable norm. */ + scale = tw9910_select_norm(priv->norm, mf->width, mf->height); + if (!scale) + return -EINVAL; + + mf->width = scale->width; + mf->height = scale->height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return tw9910_s_fmt(sd, mf); + + cfg->try_fmt = *mf; + + return 0; +} + +static int tw9910_video_probe(struct i2c_client *client) +{ + struct tw9910_priv *priv = to_tw9910(client); + s32 id; + int ret; + + /* TW9910 only use 8 or 16 bit bus width. */ + if (priv->info->buswidth != 16 && priv->info->buswidth != 8) { + dev_err(&client->dev, "bus width error\n"); + return -ENODEV; + } + + ret = tw9910_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* + * Check and show Product ID. + * So far only revisions 0 and 1 have been seen. + */ + id = i2c_smbus_read_byte_data(client, ID); + priv->revision = GET_REV(id); + id = GET_ID(id); + + if (id != 0x0b || priv->revision > 0x01) { + dev_err(&client->dev, "Product ID error %x:%x\n", + id, priv->revision); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n", + id, priv->revision); + + priv->norm = V4L2_STD_NTSC; + priv->scale = &tw9910_ntsc_scales[0]; + +done: + tw9910_s_power(&priv->subdev, 0); + + return ret; +} + +static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tw9910_g_register, + .s_register = tw9910_s_register, +#endif + .s_power = tw9910_s_power, +}; + +static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + *norm = V4L2_STD_NTSC | V4L2_STD_PAL; + + return 0; +} + +static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { + .s_std = tw9910_s_std, + .g_std = tw9910_g_std, + .s_stream = tw9910_s_stream, + .g_tvnorms = tw9910_g_tvnorms, +}; + +static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { + .enum_mbus_code = tw9910_enum_mbus_code, + .get_selection = tw9910_get_selection, + .get_fmt = tw9910_get_fmt, + .set_fmt = tw9910_set_fmt, +}; + +static const struct v4l2_subdev_ops tw9910_subdev_ops = { + .core = &tw9910_subdev_core_ops, + .video = &tw9910_subdev_video_ops, + .pad = &tw9910_subdev_pad_ops, +}; + +/* + * i2c_driver function + */ + +static int tw9910_probe(struct i2c_client *client, + const struct i2c_device_id *did) + +{ + struct tw9910_priv *priv; + struct tw9910_video_info *info; + struct i2c_adapter *adapter = + to_i2c_adapter(client->dev.parent); + int ret; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "TW9910: missing platform data!\n"); + return -EINVAL; + } + + info = client->dev.platform_data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n"); + return -EIO; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + + v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); + + priv->clk = clk_get(&client->dev, "xti"); + if (PTR_ERR(priv->clk) == -ENOENT) { + priv->clk = NULL; + } else if (IS_ERR(priv->clk)) { + dev_err(&client->dev, "Unable to get xti clock\n"); + return PTR_ERR(priv->clk); + } + + priv->pdn_gpio = gpiod_get_optional(&client->dev, "pdn", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->pdn_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"pdn\""); + ret = PTR_ERR(priv->pdn_gpio); + goto error_clk_put; + } + + ret = tw9910_video_probe(client); + if (ret < 0) + goto error_gpio_put; + + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret) + goto error_gpio_put; + + return ret; + +error_gpio_put: + if (priv->pdn_gpio) + gpiod_put(priv->pdn_gpio); +error_clk_put: + clk_put(priv->clk); + + return ret; +} + +static int tw9910_remove(struct i2c_client *client) +{ + struct tw9910_priv *priv = to_tw9910(client); + + if (priv->pdn_gpio) + gpiod_put(priv->pdn_gpio); + clk_put(priv->clk); + v4l2_device_unregister_subdev(&priv->subdev); + + return 0; +} + +static const struct i2c_device_id tw9910_id[] = { + { "tw9910", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9910_id); + +static struct i2c_driver tw9910_i2c_driver = { + .driver = { + .name = "tw9910", + }, + .probe = tw9910_probe, + .remove = tw9910_remove, + .id_table = tw9910_id, +}; + +module_i2c_driver(tw9910_i2c_driver); + +MODULE_DESCRIPTION("V4L2 driver for TW9910 video decoder"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 560738213c00..1658816a9844 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -657,31 +657,22 @@ static int vs6624_get_fmt(struct v4l2_subdev *sd, return 0; } -static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int vs6624_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(*cp)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = sensor->frame_rate.denominator; - cp->timeperframe.denominator = sensor->frame_rate.numerator; + ival->interval.numerator = sensor->frame_rate.denominator; + ival->interval.denominator = sensor->frame_rate.numerator; return 0; } -static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +static int vs6624_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) { struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; + struct v4l2_fract *tpf = &ival->interval; - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; if (tpf->numerator == 0 || tpf->denominator == 0 || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { @@ -738,8 +729,8 @@ static const struct v4l2_subdev_core_ops vs6624_core_ops = { }; static const struct v4l2_subdev_video_ops vs6624_video_ops = { - .s_parm = vs6624_s_parm, - .g_parm = vs6624_g_parm, + .s_frame_interval = vs6624_s_frame_interval, + .g_frame_interval = vs6624_g_frame_interval, .s_stream = vs6624_s_stream, }; diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index e79f72b8b858..35e81f7c0d2f 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -190,6 +190,7 @@ static long media_device_enum_links(struct media_device *mdev, ulink_desc++; } } + memset(links->reserved, 0, sizeof(links->reserved)); return 0; } @@ -218,6 +219,8 @@ static long media_device_setup_link(struct media_device *mdev, if (link == NULL) return -EINVAL; + memset(linkd->reserved, 0, sizeof(linkd->reserved)); + /* Setup the link on both entities. */ return __media_entity_setup_link(link, linkd->flags); } @@ -255,7 +258,7 @@ static long media_device_get_topology(struct media_device *mdev, memset(&kentity, 0, sizeof(kentity)); kentity.id = entity->graph_obj.id; kentity.function = entity->function; - strncpy(kentity.name, entity->name, + strlcpy(kentity.name, entity->name, sizeof(kentity.name)); if (copy_to_user(uentity, &kentity, sizeof(kentity))) @@ -263,6 +266,7 @@ static long media_device_get_topology(struct media_device *mdev, uentity++; } topo->num_entities = i; + topo->reserved1 = 0; /* Get interfaces and number of interfaces */ i = 0; @@ -298,6 +302,7 @@ static long media_device_get_topology(struct media_device *mdev, uintf++; } topo->num_interfaces = i; + topo->reserved2 = 0; /* Get pads and number of pads */ i = 0; @@ -324,6 +329,7 @@ static long media_device_get_topology(struct media_device *mdev, upad++; } topo->num_pads = i; + topo->reserved3 = 0; /* Get links and number of links */ i = 0; @@ -355,6 +361,7 @@ static long media_device_get_topology(struct media_device *mdev, ulink++; } topo->num_links = i; + topo->reserved4 = 0; return ret; } diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index f7c6d64e6031..3498551e618e 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -64,22 +64,6 @@ static inline const char *intf_type(struct media_interface *intf) return "v4l-swradio"; case MEDIA_INTF_T_V4L_TOUCH: return "v4l-touch"; - case MEDIA_INTF_T_ALSA_PCM_CAPTURE: - return "alsa-pcm-capture"; - case MEDIA_INTF_T_ALSA_PCM_PLAYBACK: - return "alsa-pcm-playback"; - case MEDIA_INTF_T_ALSA_CONTROL: - return "alsa-control"; - case MEDIA_INTF_T_ALSA_COMPRESS: - return "alsa-compress"; - case MEDIA_INTF_T_ALSA_RAWMIDI: - return "alsa-rawmidi"; - case MEDIA_INTF_T_ALSA_HWDEP: - return "alsa-hwdep"; - case MEDIA_INTF_T_ALSA_SEQUENCER: - return "alsa-sequencer"; - case MEDIA_INTF_T_ALSA_TIMER: - return "alsa-timer"; default: return "unknown-intf"; } diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index f697698fe38d..707f57a9f940 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3344,10 +3344,10 @@ static __poll_t radio_poll(struct file *file, poll_table *wait) radio_enable(btv); cmd.instance = file; cmd.event_list = wait; - cmd.result = res; + cmd.poll_mask = res; bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd); - return cmd.result; + return cmd.poll_mask; } static const struct v4l2_file_operations radio_fops = diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index da49c5567db5..08266b23826e 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -332,11 +332,15 @@ static void bttv_ir_stop(struct bttv *btv) static int get_key_pv951(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { dprintk("read error\n"); + if (rc < 0) + return rc; return -EIO; } diff --git a/drivers/media/pci/cobalt/Makefile b/drivers/media/pci/cobalt/Makefile index b328955abbd2..29eddff2f35f 100644 --- a/drivers/media/pci/cobalt/Makefile +++ b/drivers/media/pci/cobalt/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 cobalt-objs := cobalt-driver.o cobalt-irq.o cobalt-v4l2.o \ cobalt-i2c.o cobalt-omnitek.o cobalt-flash.o cobalt-cpld.o \ cobalt-alsa-main.o cobalt-alsa-pcm.o diff --git a/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c index 720e3ad93a9e..e5022b620856 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-main.c +++ b/drivers/media/pci/cobalt/cobalt-alsa-main.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ALSA interface to cobalt PCM capture streams * * Copyright 2014-2015 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 <linux/init.h> diff --git a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c index b69b258d39b9..f6a7df13cd04 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c +++ b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c @@ -1,22 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ALSA PCM device for the * ALSA interface to cobalt PCM capture streams * * Copyright 2014-2015 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 <linux/init.h> diff --git a/drivers/media/pci/cobalt/cobalt-alsa-pcm.h b/drivers/media/pci/cobalt/cobalt-alsa-pcm.h index 513fb1f71794..0e2e9c63a23e 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-pcm.h +++ b/drivers/media/pci/cobalt/cobalt-alsa-pcm.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ALSA PCM device for the * ALSA interface to cobalt PCM capture streams * * Copyright 2014-2015 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. */ int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc); diff --git a/drivers/media/pci/cobalt/cobalt-alsa.h b/drivers/media/pci/cobalt/cobalt-alsa.h index 08db699ced37..bb7f156ad3e7 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa.h +++ b/drivers/media/pci/cobalt/cobalt-alsa.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ALSA interface to cobalt PCM capture streams * * Copyright 2014-2015 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. */ struct snd_card; diff --git a/drivers/media/pci/cobalt/cobalt-cpld.c b/drivers/media/pci/cobalt/cobalt-cpld.c index bfcecef659e3..3d8026483ac3 100644 --- a/drivers/media/pci/cobalt/cobalt-cpld.c +++ b/drivers/media/pci/cobalt/cobalt-cpld.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Cobalt CPLD functions * * Copyright 2012-2015 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 <linux/delay.h> diff --git a/drivers/media/pci/cobalt/cobalt-cpld.h b/drivers/media/pci/cobalt/cobalt-cpld.h index 0fc88fd5fa7b..8c880ed14cda 100644 --- a/drivers/media/pci/cobalt/cobalt-cpld.h +++ b/drivers/media/pci/cobalt/cobalt-cpld.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Cobalt CPLD functions * * Copyright 2012-2015 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. */ #ifndef COBALT_CPLD_H diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 3f16cf3f6d74..c8b1a6206c65 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cobalt driver initialization and card probing * @@ -5,19 +6,6 @@ * * Copyright 2012-2015 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 <linux/delay.h> diff --git a/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h index 00f773ec359a..429bee4ef79c 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.h +++ b/drivers/media/pci/cobalt/cobalt-driver.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cobalt driver internal defines and structures * @@ -5,19 +6,6 @@ * * Copyright 2012-2015 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. */ #ifndef COBALT_DRIVER_H diff --git a/drivers/media/pci/cobalt/cobalt-flash.c b/drivers/media/pci/cobalt/cobalt-flash.c index 04dcaf9198d2..ef96e0f956d2 100644 --- a/drivers/media/pci/cobalt/cobalt-flash.c +++ b/drivers/media/pci/cobalt/cobalt-flash.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Cobalt NOR flash functions * * Copyright 2012-2015 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 <linux/mtd/mtd.h> diff --git a/drivers/media/pci/cobalt/cobalt-flash.h b/drivers/media/pci/cobalt/cobalt-flash.h index 8077daea51cd..605ce3d37ca3 100644 --- a/drivers/media/pci/cobalt/cobalt-flash.h +++ b/drivers/media/pci/cobalt/cobalt-flash.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Cobalt NOR flash functions * * Copyright 2012-2015 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. */ #ifndef COBALT_FLASH_H diff --git a/drivers/media/pci/cobalt/cobalt-i2c.c b/drivers/media/pci/cobalt/cobalt-i2c.c index 1a5c55673ea8..c374dae78bf7 100644 --- a/drivers/media/pci/cobalt/cobalt-i2c.c +++ b/drivers/media/pci/cobalt/cobalt-i2c.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cobalt I2C functions * @@ -5,19 +6,6 @@ * * Copyright 2012-2015 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 "cobalt-driver.h" diff --git a/drivers/media/pci/cobalt/cobalt-i2c.h b/drivers/media/pci/cobalt/cobalt-i2c.h index a4c1cfaacf95..7a9057c8bff6 100644 --- a/drivers/media/pci/cobalt/cobalt-i2c.h +++ b/drivers/media/pci/cobalt/cobalt-i2c.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cobalt I2C functions * @@ -5,19 +6,6 @@ * * Copyright 2012-2015 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. */ /* init + register i2c algo-bit adapter */ diff --git a/drivers/media/pci/cobalt/cobalt-irq.c b/drivers/media/pci/cobalt/cobalt-irq.c index b190d4f81c6e..04783e78cc12 100644 --- a/drivers/media/pci/cobalt/cobalt-irq.c +++ b/drivers/media/pci/cobalt/cobalt-irq.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cobalt interrupt handling * * Copyright 2012-2015 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 <media/i2c/adv7604.h> diff --git a/drivers/media/pci/cobalt/cobalt-irq.h b/drivers/media/pci/cobalt/cobalt-irq.h index 5119484a24d9..0b4078ce6555 100644 --- a/drivers/media/pci/cobalt/cobalt-irq.h +++ b/drivers/media/pci/cobalt/cobalt-irq.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cobalt interrupt handling * * Copyright 2012-2015 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 <linux/interrupt.h> diff --git a/drivers/media/pci/cobalt/cobalt-omnitek.c b/drivers/media/pci/cobalt/cobalt-omnitek.c index a28a8482c1d4..4c137453e679 100644 --- a/drivers/media/pci/cobalt/cobalt-omnitek.c +++ b/drivers/media/pci/cobalt/cobalt-omnitek.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Omnitek Scatter-Gather DMA Controller * * Copyright 2012-2015 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 <linux/string.h> diff --git a/drivers/media/pci/cobalt/cobalt-omnitek.h b/drivers/media/pci/cobalt/cobalt-omnitek.h index e5c6d032c6f2..129c5fccbe39 100644 --- a/drivers/media/pci/cobalt/cobalt-omnitek.h +++ b/drivers/media/pci/cobalt/cobalt-omnitek.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Omnitek Scatter-Gather DMA Controller * * Copyright 2012-2015 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. */ #ifndef COBALT_OMNITEK_H diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index def4a3b37084..e2a4c705d353 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * cobalt V4L2 API * @@ -5,19 +6,6 @@ * * Copyright 2012-2015 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 <linux/dma-mapping.h> diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.h b/drivers/media/pci/cobalt/cobalt-v4l2.h index 62be553cd8e2..dc43974b2d86 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.h +++ b/drivers/media/pci/cobalt/cobalt-v4l2.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * cobalt V4L2 API * * Copyright 2012-2015 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. */ int cobalt_nodes_register(struct cobalt *cobalt); diff --git a/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h b/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h index 9bc9ef1fd3a8..4c6ad1cee87e 100644 --- a/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h +++ b/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h b/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h index a480529f561e..6cc1ad7d98c9 100644 --- a/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h +++ b/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00235_FDMA_PACKER_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h b/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h index 602419e589d3..f0c6fe304247 100644 --- a/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h +++ b/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00389_CVI_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h index 95471c995067..27f05aca632f 100644 --- a/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h +++ b/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00460_EVCNT_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h b/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h index 384a3e156301..8a5bf008750a 100644 --- a/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h +++ b/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00473_FREEWHEEL_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h b/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h index 2a029026bf82..18bd4fcd2db8 100644 --- a/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h +++ b/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h index bdef2df5d689..86da49033cd8 100644 --- a/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h +++ b/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2014-2015 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. */ #ifndef M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_H diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c index 2531e4b81b60..93443d1457c5 100644 --- a/drivers/media/pci/cx18/cx18-alsa-main.c +++ b/drivers/media/pci/cx18/cx18-alsa-main.c @@ -32,7 +32,6 @@ #include "cx18-driver.h" #include "cx18-version.h" #include "cx18-alsa.h" -#include "cx18-alsa-mixer.h" #include "cx18-alsa-pcm.h" int cx18_alsa_debug; diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c deleted file mode 100644 index cb04c3d820e2..000000000000 --- a/drivers/media/pci/cx18/cx18-alsa-mixer.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * ALSA mixer controls for the - * ALSA interface to cx18 PCM capture streams - * - * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> - * - * 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 <linux/init.h> -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/spinlock.h> -#include <linux/videodev2.h> - -#include <media/v4l2-device.h> - -#include <sound/core.h> -#include <sound/control.h> -#include <sound/tlv.h> - -#include "cx18-alsa.h" -#include "cx18-driver.h" - -/* - * Note the cx18-av-core volume scale is funny, due to the alignment of the - * scale with another chip's range: - * - * v4l2_control value /512 indicated dB actual dB reg 0x8d4 - * 0x0000 - 0x01ff 0 -119 -96 228 - * 0x0200 - 0x02ff 1 -118 -96 228 - * ... - * 0x2c00 - 0x2dff 22 -97 -96 228 - * 0x2e00 - 0x2fff 23 -96 -96 228 - * 0x3000 - 0x31ff 24 -95 -95 226 - * ... - * 0xee00 - 0xefff 119 0 0 36 - * ... - * 0xfe00 - 0xffff 127 +8 +8 20 - */ -static inline int dB_to_cx18_av_vol(int dB) -{ - if (dB < -96) - dB = -96; - else if (dB > 8) - dB = 8; - return (dB + 119) << 9; -} - -static inline int cx18_av_vol_to_dB(int v) -{ - if (v < (23 << 9)) - v = (23 << 9); - else if (v > (127 << 9)) - v = (127 << 9); - return (v >> 9) - 119; -} - -static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - /* We're already translating values, just keep this control in dB */ - uinfo->value.integer.min = -96; - uinfo->value.integer.max = 8; - uinfo->value.integer.step = 1; - return 0; -} - -static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); - struct cx18 *cx = to_cx18(cxsc->v4l2_dev); - struct v4l2_control vctrl; - int ret; - - vctrl.id = V4L2_CID_AUDIO_VOLUME; - vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); - - snd_cx18_lock(cxsc); - ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl); - snd_cx18_unlock(cxsc); - - if (!ret) - uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); - return ret; -} - -static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); - struct cx18 *cx = to_cx18(cxsc->v4l2_dev); - struct v4l2_control vctrl; - int ret; - - vctrl.id = V4L2_CID_AUDIO_VOLUME; - vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); - - snd_cx18_lock(cxsc); - - /* Fetch current state */ - 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_s_ctrl(cx->sd_av->ctrl_handler, &vctrl); - if (!ret) - ret = 1; /* Indicate control was changed w/o error */ - } - snd_cx18_unlock(cxsc); - - return ret; -} - - -/* This is a bit of overkill, the slider is already in dB internally */ -static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0); - -static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog TV Capture Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ, - .info = snd_cx18_mixer_tv_volume_info, - .get = snd_cx18_mixer_tv_volume_get, - .put = snd_cx18_mixer_tv_volume_put, - .tlv.p = snd_cx18_mixer_tv_vol_db_scale -}; - -/* FIXME - add mute switch and balance, bass, treble sliders: - V4L2_CID_AUDIO_MUTE - - V4L2_CID_AUDIO_BALANCE - - V4L2_CID_AUDIO_BASS - V4L2_CID_AUDIO_TREBLE -*/ - -/* FIXME - add stereo, lang1, lang2, mono menu */ -/* FIXME - add CS5345 I2S volume for HVR-1600 */ - -int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc) -{ - struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; - struct snd_card *sc = cxsc->sc; - int ret; - - strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername)); - - ret = snd_ctl_add(sc, snd_ctl_new1(&snd_cx18_mixer_tv_vol, cxsc)); - if (ret) { - CX18_ALSA_WARN("%s: failed to add %s control, err %d\n", - __func__, snd_cx18_mixer_tv_vol.name, ret); - } - return ret; -} diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.h b/drivers/media/pci/cx18/cx18-alsa-mixer.h deleted file mode 100644 index 3aed123955dd..000000000000 --- a/drivers/media/pci/cx18/cx18-alsa-mixer.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * ALSA mixer controls for the - * ALSA interface to cx18 PCM capture streams - * - * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> - * - * 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. - */ - -int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc); diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c index 53f4d6bf81fb..010f39eafce1 100644 --- a/drivers/media/pci/cx18/cx18-dvb.c +++ b/drivers/media/pci/cx18/cx18-dvb.c @@ -72,7 +72,7 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE }; @@ -86,7 +86,7 @@ static struct s5h1411_config hcw_s5h1411_config = { .qam_if = S5H1411_IF_4000, .inversion = S5H1411_INVERSION_ON, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct tda18271_std_map hauppauge_tda18271_std_map = { diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index d8c3637e492e..20b3cb17f97f 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -89,9 +89,8 @@ static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages) return -ENOMEM; } - dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)buf->vaddr, - nr_pages << PAGE_SHIFT); + dprintk(1, "vmalloc is at addr %p, size=%d\n", + buf->vaddr, nr_pages << PAGE_SHIFT); memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); buf->nr_pages = nr_pages; diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 3622521431f5..3a1c55187b2a 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -771,11 +771,46 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885] = { + .name = "Hauppauge WinTV-QuadHD-DVB(885)", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC] = { .name = "Hauppauge WinTV-QuadHD-ATSC", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885] = { + .name = "Hauppauge WinTV-QuadHD-ATSC(885)", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1265_K4] = { + .name = "Hauppauge WinTV-HVR-1265(161111)", + .porta = CX23885_ANALOG_VIDEO, + .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN6_CH1, + .amux = CX25840_AUDIO7, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, + } }, + }, + [CX23885_BOARD_HAUPPAUGE_STARBURST2] = { + .name = "Hauppauge WinTV-Starburst2", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -1028,6 +1063,10 @@ struct cx23885_subid cx23885_subids[] = { .subdevice = 0x7133, .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE, }, { + .subvendor = 0x0070, + .subdevice = 0x7137, + .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE, + }, { .subvendor = 0x18ac, .subdevice = 0xdb98, .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2, @@ -1087,6 +1126,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x6b18, .card = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC, /* Tuner Pair 2 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x2a18, + .card = CX23885_BOARD_HAUPPAUGE_HVR1265_K4, /* Hauppauge WinTV HVR-1265 (Model 161xx1, Hybrid ATSC/QAM-B) */ + }, { + .subvendor = 0x0070, + .subdevice = 0xf02a, + .card = CX23885_BOARD_HAUPPAUGE_STARBURST2, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1287,25 +1334,28 @@ 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: + case 161111: + /* WinTV-HVR-1265 K4 (PCIe, Analog/ATSC/QAM-B) */ + break; + case 166100: /* 888 version, hybrid */ + case 166200: /* 885 version, DVB only */ /* WinTV-QuadHD (DVB) Tuner Pair 1 (PCIe, IR, half height, DVB-T/T2/C, DVB-T/T2/C */ break; - case 166101: + case 166101: /* 888 version, hybrid */ + case 166201: /* 885 version, DVB only */ /* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height, DVB-T/T2/C, DVB-T/T2/C */ break; - case 165100: - /* - * WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height, - * ATSC, ATSC - */ + case 165100: /* 888 version, hybrid */ + case 165200: /* 885 version, digital only */ + /* WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height, + * ATSC/QAM-B, ATSC/QAM-B */ break; - case 165101: - /* - * WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height, - * ATSC, ATSC - */ + case 165101: /* 888 version, hybrid */ + case 165201: /* 885 version, digital only */ + /* WinTV-QuadHD (ATSC) Tuner Pair 2 (PCIe, IR, half height, + * ATSC/QAM-B, ATSC/QAM-B */ break; default: pr_warn("%s: warning: unknown hauppauge model #%d\n", @@ -1778,8 +1828,7 @@ 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: - case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + case CX23885_BOARD_HAUPPAUGE_STARBURST2: /* * HVR5525 GPIO Details: * GPIO-00 IR_WIDE @@ -1809,6 +1858,22 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) * card does not have any GPIO's connected to subcomponents. */ break; + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885: + /* + * GPIO-08 TER1_RESN + * GPIO-09 TER2_RESN + */ + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); + cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); + msleep(100); + cx23885_gpio_set(dev, GPIO_8 | GPIO_9); + msleep(100); + break; } } @@ -2054,8 +2119,12 @@ 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_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_STARBURST2: case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885: case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -2194,6 +2263,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_STARBURST2: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -2201,8 +2271,11 @@ 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_HVR1265_K4: case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885: case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885: 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; @@ -2259,6 +2332,9 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_COMPRO_VIDEOMATE_E800: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_MYGICA_X8506: @@ -2286,6 +2362,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); if (dev->sd_cx25840) { + /* set host data for clk_freq configuration */ + v4l2_set_subdev_hostdata(dev->sd_cx25840, + &dev->clk_freq); + dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE; v4l2_subdev_call(dev->sd_cx25840, core, load_fw); } diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 8f63df1cb418..019fac49db5b 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -839,10 +839,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) /* Configure the internal memory */ if (dev->pci->device == 0x8880) { - /* Could be 887 or 888, assume a default */ - dev->bridge = CX23885_BRIDGE_887; + /* Could be 887 or 888, assume an 888 default */ + dev->bridge = CX23885_BRIDGE_888; /* Apply a sensible clock frequency for the PCIe bridge */ - dev->clk_freq = 25000000; + dev->clk_freq = 50000000; dev->sram_channels = cx23887_sram_channels; } else if (dev->pci->device == 0x8852) { @@ -869,10 +869,28 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_card_list(dev); } + if (dev->pci->device == 0x8852) { + /* no DIF on cx23885, so no analog tuner support possible */ + if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) + dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885; + else if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) + dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885; + } + /* If the user specific a clk freq override, apply it */ if (cx23885_boards[dev->board].clk_freq > 0) dev->clk_freq = cx23885_boards[dev->board].clk_freq; + if (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE && + dev->pci->subsystem_device == 0x7137) { + /* Hauppauge ImpactVCBe device ID 0x7137 is populated + * with an 888, and a 25Mhz crystal, instead of the + * usual third overtone 50Mhz. The default clock rate must + * be overridden so the cx25840 is properly configured + */ + dev->clk_freq = 25000000; + } + dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); cx23885_irq_add(dev, 0x001f00); @@ -965,7 +983,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - call_all(dev, core, s_power, 0); + call_all(dev, tuner, standby); cx23885_ir_init(dev); if (dev->board == CX23885_BOARD_VIEWCAST_460E) { diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 700422b538c0..114d9bcbe4f4 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -193,7 +193,7 @@ static struct s5h1409_config hauppauge_generic_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct tda10048_config hauppauge_hvr1200_config = { @@ -225,7 +225,7 @@ static struct s5h1409_config hauppauge_ezqam_config = { .qam_if = 4000, .inversion = S5H1409_INVERSION_ON, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1409_config hauppauge_hvr1800lp_config = { @@ -235,7 +235,7 @@ static struct s5h1409_config hauppauge_hvr1800lp_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1409_config hauppauge_hvr1500_config = { @@ -244,7 +244,7 @@ static struct s5h1409_config hauppauge_hvr1500_config = { .gpio = S5H1409_GPIO_OFF, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct mt2131_config hauppauge_generic_tunerconfig = { @@ -264,7 +264,7 @@ static struct s5h1409_config hauppauge_hvr1500q_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1409_config dvico_s5h1409_config = { @@ -274,7 +274,7 @@ static struct s5h1409_config dvico_s5h1409_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1411_config dvico_s5h1411_config = { @@ -284,7 +284,7 @@ static struct s5h1411_config dvico_s5h1411_config = { .vsb_if = S5H1411_IF_44000, .inversion = S5H1411_INVERSION_OFF, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1411_config hcw_s5h1411_config = { @@ -294,7 +294,7 @@ static struct s5h1411_config hcw_s5h1411_config = { .qam_if = S5H1411_IF_4000, .inversion = S5H1411_INVERSION_ON, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { @@ -930,6 +930,18 @@ static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = { .agc = 0x99, }; +static struct lgdt3306a_config hauppauge_hvr1265k4_config = { + .i2c_addr = 0x59, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1194,6 +1206,8 @@ static int dvb_register(struct cx23885_tsport *port) struct si2157_config si2157_config; struct ts2020_config ts2020_config; struct m88ds3103_platform_data m88ds3103_pdata; + struct m88rs6000t_config m88rs6000t_config = {}; + struct a8293_platform_data a8293_pdata = {}; struct i2c_board_info info; struct i2c_adapter *adapter; struct i2c_client *client_demod = NULL, *client_tuner = NULL; @@ -2217,9 +2231,10 @@ static int dvb_register(struct cx23885_tsport *port) } port->i2c_client_tuner = client_tuner; break; - case CX23885_BOARD_HAUPPAUGE_HVR5525: { - struct m88rs6000t_config m88rs6000t_config; - struct a8293_platform_data a8293_pdata = {}; + case CX23885_BOARD_HAUPPAUGE_STARBURST2: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; switch (port->nr) { @@ -2228,7 +2243,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach frontend */ fe0->dvb.frontend = dvb_attach(m88ds3103_attach, &hauppauge_hvr5525_m88ds3103_config, - &dev->i2c_bus[0].i2c_adap, &adapter); + &i2c_bus->i2c_adap, &adapter); if (fe0->dvb.frontend == NULL) break; @@ -2239,7 +2254,7 @@ static int dvb_register(struct cx23885_tsport *port) info.addr = 0x0b; info.platform_data = &a8293_pdata; request_module("a8293"); - client_sec = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + client_sec = i2c_new_device(&i2c_bus->i2c_adap, &info); if (!client_sec || !client_sec->dev.driver) goto frontend_detach; if (!try_module_get(client_sec->dev.driver->owner)) { @@ -2281,7 +2296,7 @@ static int dvb_register(struct cx23885_tsport *port) 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); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); if (!client_demod || !client_demod->dev.driver) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { @@ -2299,7 +2314,7 @@ static int dvb_register(struct cx23885_tsport *port) 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); + client_tuner = i2c_new_device(&i2c_bus2->i2c_adap, &info); if (!client_tuner || !client_tuner->dev.driver) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); @@ -2317,8 +2332,10 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; - } case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885: + pr_info("%s(): board=%d port=%d\n", __func__, + dev->board, port->nr); switch (port->nr) { /* port b - Terrestrial/cable */ case 1: @@ -2416,6 +2433,9 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885: + pr_info("%s(): board=%d port=%d\n", __func__, + dev->board, port->nr); switch (port->nr) { /* port b - Terrestrial/cable */ case 1: @@ -2490,7 +2510,41 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + switch (port->nr) { + /* port c - Terrestrial/cable */ + case 2: + /* attach frontend */ + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_hvr1265k4_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend == NULL) + break; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; + si2157_config.inversion = 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) + goto frontend_detach; + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + client_tuner = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: pr_info("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->name); @@ -2514,8 +2568,8 @@ static int dvb_register(struct cx23885_tsport *port) fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl; #endif - /* Put the analog decoder in standby to keep it quiet */ - call_all(dev, core, s_power, 0); + /* Put the tuner in standby to keep it quiet */ + call_all(dev, tuner, standby); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 0f4e54294bb7..be49589a61d2 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -94,6 +94,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) case CX23885_BOARD_DVBSKY_S950: case CX23885_BOARD_DVBSKY_S952: case CX23885_BOARD_DVBSKY_T982: + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: /* * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar @@ -153,6 +154,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev) case CX23885_BOARD_DVBSKY_S950: case CX23885_BOARD_DVBSKY_S952: case CX23885_BOARD_DVBSKY_T982: + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -283,6 +285,7 @@ int cx23885_input_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: /* Integrated CX2388[58] IR controller */ allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; /* The grey Hauppauge RC-5 remote */ diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index a03dcb662953..f8a3deadc77a 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -263,6 +263,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || (dev->board == CX23885_BOARD_MYGICA_X8507) || (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) || @@ -993,7 +994,8 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || - (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111)) + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4)) fe = &dev->ts1.analog_fe; if (fe && fe->ops.tuner_ops.set_analog_params) { @@ -1022,6 +1024,7 @@ int cx23885_set_frequency(struct file *file, void *priv, switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: case CX23885_BOARD_HAUPPAUGE_HVR1850: ret = cx23885_set_freq_via_ops(dev, f); break; diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 2a17209eb4f6..d54c7ee1ab21 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -107,6 +107,10 @@ #define CX23885_BOARD_VIEWCAST_460E 55 #define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56 #define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC 57 +#define CX23885_BOARD_HAUPPAUGE_HVR1265_K4 58 +#define CX23885_BOARD_HAUPPAUGE_STARBURST2 59 +#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885 60 +#define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885 61 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 04aa4a68a0ae..040c6c251d3a 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -867,6 +867,10 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->nr = ++cx25821_devcount; sprintf(dev->name, "cx25821[%d]", dev->nr); + if (dev->nr >= ARRAY_SIZE(card)) { + CX25821_INFO("dev->nr >= %zd", ARRAY_SIZE(card)); + return -ENODEV; + } if (dev->pci->device != 0x8210) { pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", __func__, dev->pci->device); @@ -882,9 +886,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->channels[i].sram_channels = &cx25821_sram_channels[i]; } - if (dev->nr > 1) - CX25821_INFO("dev->nr > 1!"); - /* board config */ dev->board = 1; /* card[dev->nr]; */ dev->_max_num_decoders = MAX_DECODERS; diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index 9740326bc93f..ab09bb55cf45 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -292,8 +292,8 @@ static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages) return -ENOMEM; } - dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)buf->vaddr, nr_pages << PAGE_SHIFT); + dprintk(1, "vmalloc is at addr %p, size=%d\n", + buf->vaddr, nr_pages << PAGE_SHIFT); memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); buf->nr_pages = nr_pages; @@ -656,8 +656,8 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, { struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; - int left = value->value.integer.value[0]; - int right = value->value.integer.value[1]; + u16 left = value->value.integer.value[0]; + u16 right = value->value.integer.value[1]; int v, b; /* Pass volume & balance onto any WM8775 */ diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 6df21b29ea17..4c92d2388c26 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3592,7 +3592,7 @@ static void cx88_card_setup(struct cx88_core *core) ctl.fname); call_all(core, tuner, s_config, &xc2028_cfg); } - call_all(core, core, s_power, 0); + call_all(core, tuner, standby); } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 49a335f4603e..2f886140dd2e 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -558,7 +558,7 @@ static const struct s5h1409_config pinnacle_pctv_hd_800i_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK, }; static const struct s5h1409_config dvico_hdtv5_pci_nano_config = { @@ -567,7 +567,7 @@ static const struct s5h1409_config dvico_hdtv5_pci_nano_config = { .gpio = S5H1409_GPIO_OFF, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static const struct s5h1409_config kworld_atsc_120_config = { @@ -576,7 +576,7 @@ static const struct s5h1409_config kworld_atsc_120_config = { .gpio = S5H1409_GPIO_OFF, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = { @@ -599,7 +599,7 @@ static const struct zl10353_config cx88_geniatech_x8000_mt = { static const struct s5h1411_config dvico_fusionhdtv7_config = { .output_mode = S5H1411_SERIAL_OUTPUT, .gpio = S5H1411_GPIO_ON, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, .qam_if = S5H1411_IF_44000, .vsb_if = S5H1411_IF_44000, .inversion = S5H1411_INVERSION_OFF, @@ -1631,8 +1631,8 @@ static int dvb_register(struct cx8802_dev *dev) if (fe1) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; - /* Put the analog decoder in standby to keep it quiet */ - call_all(core, core, s_power, 0); + /* Put the tuner in standby to keep it quiet */ + call_all(core, tuner, standby); /* register everything */ res = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 4e9953e61a12..6f4e6923a91a 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -180,7 +180,7 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); cx88_ir_handle_key(ir); - missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000); + missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000LL); if (missed > 1) ir_dprintk("Missed ticks %ld\n", missed - 1); @@ -200,7 +200,7 @@ static int __cx88_ir_start(void *priv) if (ir->polling) { hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ir->timer.function = cx88_ir_work; - hrtimer_start(&ir->timer, ir->polling * 1000000, + hrtimer_start(&ir->timer, ir->polling * 1000000LL, HRTIMER_MODE_REL); } if (ir->sampling) { diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index f43d0b83fc0c..a422dde2f34a 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -13,6 +13,7 @@ config DVB_DDBRIDGE select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT select DVB_MXL5XX if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for cards with the Digital Devices PCI express bridge: - Octopus PCIe Bridge diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index f58fdec50eab..745b37d07558 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -10,6 +10,3 @@ obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o ccflags-y += -Idrivers/media/dvb-frontends/ ccflags-y += -Idrivers/media/tuners/ - -# For the staging CI driver cxd2099 -ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.c b/drivers/media/pci/ddbridge/ddbridge-ci.c index 5828111487b0..a9dbc4ebf94f 100644 --- a/drivers/media/pci/ddbridge/ddbridge-ci.c +++ b/drivers/media/pci/ddbridge/ddbridge-ci.c @@ -172,6 +172,7 @@ static void ci_attach(struct ddb_port *port) memcpy(&ci->en, &en_templ, sizeof(en_templ)); ci->en.data = ci; port->en = &ci->en; + port->en_freedata = 1; ci->port = port; ci->nr = port->nr - 2; } @@ -304,6 +305,7 @@ static void ci_xo2_attach(struct ddb_port *port) memcpy(&ci->en, &en_xo2_templ, sizeof(en_xo2_templ)); ci->en.data = ci; port->en = &ci->en; + port->en_freedata = 1; ci->port = port; ci->nr = port->nr - 2; ci->port->creg = 0; @@ -311,38 +313,58 @@ static void ci_xo2_attach(struct ddb_port *port) write_creg(ci, 0x08, 0x08); } -static struct cxd2099_cfg cxd_cfg = { +static const struct cxd2099_cfg cxd_cfgtmpl = { .bitrate = 72000, - .adr = 0x40, .polarity = 1, .clock_mode = 1, .max_i2c = 512, }; +static int ci_cxd2099_attach(struct ddb_port *port, u32 bitrate) +{ + struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl; + struct i2c_client *client; + + cxd_cfg.bitrate = bitrate; + cxd_cfg.en = &port->en; + + client = dvb_module_probe("cxd2099", NULL, &port->i2c->adap, + 0x40, &cxd_cfg); + if (!client) + goto err; + + port->dvb[0].i2c_client[0] = client; + port->en_freedata = 0; + return 0; + +err: + dev_err(port->dev->dev, "CXD2099AR attach failed\n"); + return -ENODEV; +} + int ddb_ci_attach(struct ddb_port *port, u32 bitrate) { + int ret; + switch (port->type) { case DDB_CI_EXTERNAL_SONY: - cxd_cfg.bitrate = bitrate; - port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap); - if (!port->en) + ret = ci_cxd2099_attach(port, bitrate); + if (ret) return -ENODEV; break; - case DDB_CI_EXTERNAL_XO2: case DDB_CI_EXTERNAL_XO2_B: ci_xo2_attach(port); - if (!port->en) - return -ENODEV; break; - case DDB_CI_INTERNAL: ci_attach(port); - if (!port->en) - return -ENODEV; break; + default: + return -ENODEV; } + if (!port->en) + return -ENODEV; dvb_ca_en50221_init(port->dvb[0].adap, port->en, 0, 1); return 0; } @@ -353,7 +375,14 @@ void ddb_ci_detach(struct ddb_port *port) dvb_unregister_device(port->dvb[0].dev); if (port->en) { dvb_ca_en50221_release(port->en); - kfree(port->en->data); + + dvb_module_release(port->dvb[0].i2c_client[0]); + port->dvb[0].i2c_client[0] = NULL; + + /* free alloc'ed memory if needed */ + if (port->en_freedata) + kfree(port->en->data); + port->en = NULL; } } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index f9bee36f1cad..90687eff5909 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -999,37 +999,21 @@ static int tuner_attach_tda18212(struct ddb_input *input, u32 porttype) .if_dvbt2_8 = 4000, .if_dvbc = 5000, }; - struct i2c_board_info board_info = { - .type = "tda18212", - .platform_data = &config, - }; - - if (input->nr & 1) - board_info.addr = 0x63; - else - board_info.addr = 0x60; + u8 addr = (input->nr & 1) ? 0x63 : 0x60; /* due to a hardware quirk with the I2C gate on the stv0367+tda18212 * combo, the tda18212 must be probed by reading it's id _twice_ when * cold started, or it very likely will fail. */ if (porttype == DDB_TUNER_DVBCT_ST) - tuner_tda18212_ping(input, board_info.addr); - - request_module(board_info.type); - - /* perform tuner init/attach */ - client = i2c_new_device(adapter, &board_info); - if (!client || !client->dev.driver) - goto err; + tuner_tda18212_ping(input, addr); - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); + /* perform tuner probe/init/attach */ + client = dvb_module_probe("tda18212", NULL, adapter, addr, &config); + if (!client) goto err; - } dvb->i2c_client[0] = client; - return 0; err: dev_err(dev, "TDA18212 tuner not found. Device is not fully operational.\n"); @@ -1253,7 +1237,6 @@ static void dvb_input_detach(struct ddb_input *input) { struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; struct dvb_demux *dvbdemux = &dvb->demux; - struct i2c_client *client; switch (dvb->attached) { case 0x31: @@ -1263,13 +1246,8 @@ static void dvb_input_detach(struct ddb_input *input) dvb_unregister_frontend(dvb->fe); /* fallthrough */ case 0x30: - client = dvb->i2c_client[0]; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - dvb->i2c_client[0] = NULL; - client = NULL; - } + dvb_module_release(dvb->i2c_client[0]); + dvb->i2c_client[0] = NULL; if (dvb->fe2) dvb_frontend_detach(dvb->fe2); diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 095457737bc1..f223dc6c9963 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -276,6 +276,7 @@ struct ddb_port { struct ddb_input *input[2]; struct ddb_output *output; struct dvb_ca_en50221 *en; + u8 en_freedata; struct ddb_dvb dvb[2]; u32 gap; u32 obr; diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 6c4444b31f4b..7d768ec0f824 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017 Intel Corporation. - * - * 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. + * Copyright (C) 2017 Intel Corporation * * Based partially on Intel IPU4 driver written by * Sakari Ailus <sakari.ailus@linux.intel.com> @@ -526,6 +518,8 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) unsigned int i, maxloops = 1000; /* Disable CSI receiver and MIPI backend devices */ + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); @@ -1035,6 +1029,7 @@ static void cio2_vb2_stop_streaming(struct vb2_queue *vq) "failed to stop sensor streaming\n"); cio2_hw_exit(cio2, q); + synchronize_irq(cio2->pci_dev->irq); cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR); media_pipeline_stop(&q->vdev.entity); pm_runtime_put(&cio2->pci_dev->dev); @@ -1976,6 +1971,7 @@ static int __maybe_unused cio2_suspend(struct device *dev) /* Stop stream */ cio2_hw_exit(cio2, q); + synchronize_irq(pci_dev->irq); pm_runtime_force_suspend(dev); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index 78a5799f08e7..240635be7a31 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -1,15 +1,5 @@ -/* - * Copyright (c) 2017 Intel Corporation. - * - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 Intel Corporation */ #ifndef __IPU3_CIO2_H #define __IPU3_CIO2_H diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c index 029f52733f70..c1856f609d2c 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-main.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c @@ -20,7 +20,6 @@ #include "ivtv-driver.h" #include "ivtv-version.h" #include "ivtv-alsa.h" -#include "ivtv-alsa-mixer.h" #include "ivtv-alsa-pcm.h" #include <sound/core.h> @@ -160,15 +159,7 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev) /* (4) Set the driver ID and name strings */ snd_ivtv_card_set_names(itvsc); - /* (5) Create other components: mixer, PCM, & proc files */ -#if 0 - ret = snd_ivtv_mixer_create(itvsc); - if (ret) { - IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d: proceeding anyway\n", - __func__, ret); - } -#endif - + /* (5) Create other components: PCM, & proc files */ ret = snd_ivtv_pcm_create(itvsc); if (ret) { IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c deleted file mode 100644 index aee453fcff37..000000000000 --- a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * ALSA mixer controls for the - * ALSA interface to ivtv PCM capture streams - * - * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net> - * - * 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 "ivtv-alsa.h" -#include "ivtv-alsa-mixer.h" -#include "ivtv-driver.h" - -#include <linux/videodev2.h> - -#include <sound/core.h> -#include <sound/control.h> -#include <sound/tlv.h> - -/* - * Note the cx25840-core volume scale is funny, due to the alignment of the - * scale with another chip's range: - * - * v4l2_control value /512 indicated dB actual dB reg 0x8d4 - * 0x0000 - 0x01ff 0 -119 -96 228 - * 0x0200 - 0x02ff 1 -118 -96 228 - * ... - * 0x2c00 - 0x2dff 22 -97 -96 228 - * 0x2e00 - 0x2fff 23 -96 -96 228 - * 0x3000 - 0x31ff 24 -95 -95 226 - * ... - * 0xee00 - 0xefff 119 0 0 36 - * ... - * 0xfe00 - 0xffff 127 +8 +8 20 - */ -static inline int dB_to_cx25840_vol(int dB) -{ - if (dB < -96) - dB = -96; - else if (dB > 8) - dB = 8; - return (dB + 119) << 9; -} - -static inline int cx25840_vol_to_dB(int v) -{ - if (v < (23 << 9)) - v = (23 << 9); - else if (v > (127 << 9)) - v = (127 << 9); - return (v >> 9) - 119; -} - -static int snd_ivtv_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - /* We're already translating values, just keep this control in dB */ - uinfo->value.integer.min = -96; - uinfo->value.integer.max = 8; - uinfo->value.integer.step = 1; - return 0; -} - -static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl); - struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); - struct v4l2_control vctrl; - int ret; - - vctrl.id = V4L2_CID_AUDIO_VOLUME; - vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); - - snd_ivtv_lock(itvsc); - ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl); - snd_ivtv_unlock(itvsc); - - if (!ret) - uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value); - return ret; -} - -static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl); - struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); - struct v4l2_control vctrl; - int ret; - - vctrl.id = V4L2_CID_AUDIO_VOLUME; - vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); - - snd_ivtv_lock(itvsc); - - /* Fetch current state */ - 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_s_ctrl(itv->sd_audio->ctrl_handler, &vctrl); - if (!ret) - ret = 1; /* Indicate control was changed w/o error */ - } - snd_ivtv_unlock(itvsc); - - return ret; -} - - -/* This is a bit of overkill, the slider is already in dB internally */ -static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0); - -static struct snd_kcontrol_new snd_ivtv_mixer_tv_vol __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog TV Capture Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ, - .info = snd_ivtv_mixer_tv_volume_info, - .get = snd_ivtv_mixer_tv_volume_get, - .put = snd_ivtv_mixer_tv_volume_put, - .tlv.p = snd_ivtv_mixer_tv_vol_db_scale -}; - -/* FIXME - add mute switch and balance, bass, treble sliders: - V4L2_CID_AUDIO_MUTE - - V4L2_CID_AUDIO_BALANCE - - V4L2_CID_AUDIO_BASS - V4L2_CID_AUDIO_TREBLE -*/ - -/* FIXME - add stereo, lang1, lang2, mono menu */ -/* FIXME - add I2S volume */ - -int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc) -{ - struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; - struct snd_card *sc = itvsc->sc; - int ret; - - strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername)); - - ret = snd_ctl_add(sc, snd_ctl_new1(&snd_ivtv_mixer_tv_vol, itvsc)); - if (ret) { - IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n", - __func__, snd_ivtv_mixer_tv_vol.name, ret); - } - return ret; -} diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h deleted file mode 100644 index 382bc36bc529..000000000000 --- a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * ALSA mixer controls for the - * ALSA interface to ivtv PCM capture streams - * - * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net> - * - * 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. - */ - -int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 621b2f613d81..8e62b8be6529 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -346,8 +346,8 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, /* Not fatal, but will have undesirable results */ if ((unsigned long)source & 3) - IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n", - (unsigned long)source); + IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (%p)\n", + source); if (dest_offset & 3) IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); @@ -357,12 +357,10 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, /* Check Source */ if (!access_ok(VERIFY_READ, source + dest_offset, count)) { - IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n", - (unsigned long)source); + IVTVFB_WARN("Invalid userspace pointer %p\n", source); - IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n", - dest_offset, (unsigned long)source, - count); + IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n", + dest_offset, source, count); return -EINVAL; } diff --git a/drivers/media/pci/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c deleted file mode 100644 index 4155c838a18a..000000000000 --- a/drivers/media/pci/mantis/mantis_vp3028.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - Mantis VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "mantis_common.h" -#include "mantis_vp3028.h" - -struct zl10353_config mantis_vp3028_config = { - .demod_address = 0x0f, -}; - -#define MANTIS_MODEL_NAME "VP-3028" -#define MANTIS_DEV_TYPE "DVB-T" - -struct mantis_hwconfig vp3028_mantis_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_188, - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, -}; diff --git a/drivers/media/pci/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h deleted file mode 100644 index 34130d29e0aa..000000000000 --- a/drivers/media/pci/mantis/mantis_vp3028.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Mantis VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP3028_H -#define __MANTIS_VP3028_H - -#include <media/dvb_frontend.h> -#include "mantis_common.h" -#include "zl10353.h" - -#define MANTIS_VP_3028_DVB_T 0x0028 - -extern struct zl10353_config mantis_vp3028_config; -extern struct mantis_hwconfig vp3028_mantis_config; - -#endif /* __MANTIS_VP3028_H */ diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig index 637d506b23c5..e06d019996f3 100644 --- a/drivers/media/pci/ngene/Kconfig +++ b/drivers/media/pci/ngene/Kconfig @@ -8,6 +8,13 @@ config DVB_NGENE select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0910 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6111 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for Micronas PCI express cards with nGene bridge. diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile index e4208f5ed215..ec450ad19281 100644 --- a/drivers/media/pci/ngene/Makefile +++ b/drivers/media/pci/ngene/Makefile @@ -9,6 +9,3 @@ obj-$(CONFIG_DVB_NGENE) += ngene.o ccflags-y += -Idrivers/media/dvb-frontends/ ccflags-y += -Idrivers/media/tuners/ - -# For the staging CI driver cxd2099 -ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index bb49620540c5..65fc8f23ad86 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -23,6 +23,8 @@ * http://www.gnu.org/copyleft/gpl.html */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/pci.h> @@ -40,22 +42,101 @@ #include "drxk.h" #include "drxd.h" #include "dvb-pll.h" +#include "stv0367.h" +#include "stv0367_priv.h" +#include "tda18212.h" +#include "cxd2841er.h" +#include "stv0910.h" +#include "stv6111.h" +#include "lnbh25.h" + +/****************************************************************************/ +/* I2C transfer functions used for demod/tuner probing***********************/ +/****************************************************************************/ + +static int i2c_io(struct i2c_adapter *adapter, u8 adr, + u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = wbuf, .len = wlen }, + {.addr = adr, .flags = I2C_M_RD, + .buf = rbuf, .len = rlen } }; + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} + +static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, + .buf = data, .len = len}; + + return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1; +} + +static int i2c_write_reg(struct i2c_adapter *adap, u8 adr, + u8 reg, u8 val) +{ + u8 msg[2] = {reg, val}; + + return i2c_write(adap, adr, msg, 2); +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +{ + struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +} + +static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, + u16 reg, u8 *val) +{ + u8 msg[2] = {reg >> 8, reg & 0xff}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2}, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1} }; + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} +static int i2c_read_regs(struct i2c_adapter *adapter, + u8 adr, u8 reg, u8 *val, u8 len) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1}, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = len} }; + + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} + +static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val) +{ + return i2c_read_regs(adapter, adr, reg, val, 1); +} /****************************************************************************/ /* Demod/tuner attachment ***************************************************/ /****************************************************************************/ +static struct i2c_adapter *i2c_adapter_from_chan(struct ngene_channel *chan) +{ + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + if (chan->number < 2) + return &chan->dev->channel[0].i2c_adapter; + + return &chan->dev->channel[1].i2c_adapter; +} + static int tuner_attach_stv6110(struct ngene_channel *chan) { - struct i2c_adapter *i2c; + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); struct stv090x_config *feconf = (struct stv090x_config *) chan->dev->card_info->fe_config[chan->number]; struct stv6110x_config *tunerconf = (struct stv6110x_config *) chan->dev->card_info->tuner_config[chan->number]; const struct stv6110x_devctl *ctl; - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ if (chan->number < 2) i2c = &chan->dev->channel[0].i2c_adapter; else @@ -63,7 +144,7 @@ static int tuner_attach_stv6110(struct ngene_channel *chan) ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); if (ctl == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); + dev_err(pdev, "No STV6110X found!\n"); return -ENODEV; } @@ -82,6 +163,23 @@ static int tuner_attach_stv6110(struct ngene_channel *chan) return 0; } +static int tuner_attach_stv6111(struct ngene_channel *chan) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); + struct dvb_frontend *fe; + u8 adr = 4 + ((chan->number & 1) ? 0x63 : 0x60); + + fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr); + if (!fe) { + fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr & ~4); + if (!fe) { + dev_err(pdev, "stv6111_attach() failed!\n"); + return -ENODEV; + } + } + return 0; +} static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { @@ -100,35 +198,109 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) static int tuner_attach_tda18271(struct ngene_channel *chan) { - struct i2c_adapter *i2c; + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); struct dvb_frontend *fe; - i2c = &chan->dev->channel[0].i2c_adapter; if (chan->fe->ops.i2c_gate_ctrl) chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60); if (chan->fe->ops.i2c_gate_ctrl) chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); if (!fe) { - printk(KERN_ERR "No TDA18271 found!\n"); + dev_err(pdev, "No TDA18271 found!\n"); return -ENODEV; } return 0; } +static int tuner_tda18212_ping(struct ngene_channel *chan, + struct i2c_adapter *i2c, + unsigned short adr) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + u8 tda_id[2]; + u8 subaddr = 0x00; + + dev_dbg(pdev, "stv0367-tda18212 tuner ping\n"); + if (chan->fe->ops.i2c_gate_ctrl) + chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); + + if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0) + dev_dbg(pdev, "tda18212 ping 1 fail\n"); + if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0) + dev_warn(pdev, "tda18212 ping failed, expect problems\n"); + + if (chan->fe->ops.i2c_gate_ctrl) + chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); + + return 0; +} + +static int tuner_attach_tda18212(struct ngene_channel *chan, u32 dmdtype) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); + struct i2c_client *client; + struct tda18212_config config = { + .fe = chan->fe, + .if_dvbt_6 = 3550, + .if_dvbt_7 = 3700, + .if_dvbt_8 = 4150, + .if_dvbt2_6 = 3250, + .if_dvbt2_7 = 4000, + .if_dvbt2_8 = 4000, + .if_dvbc = 5000, + }; + u8 addr = (chan->number & 1) ? 0x63 : 0x60; + + /* + * due to a hardware quirk with the I2C gate on the stv0367+tda18212 + * combo, the tda18212 must be probed by reading it's id _twice_ when + * cold started, or it very likely will fail. + */ + if (dmdtype == DEMOD_TYPE_STV0367) + tuner_tda18212_ping(chan, i2c, addr); + + /* perform tuner probe/init/attach */ + client = dvb_module_probe("tda18212", NULL, i2c, addr, &config); + if (!client) + goto err; + + chan->i2c_client[0] = client; + chan->i2c_client_fe = 1; + + return 0; +err: + dev_err(pdev, "TDA18212 tuner not found. Device is not fully operational.\n"); + return -ENODEV; +} + static int tuner_attach_probe(struct ngene_channel *chan) { - if (chan->demod_type == 0) + switch (chan->demod_type) { + case DEMOD_TYPE_STV090X: return tuner_attach_stv6110(chan); - if (chan->demod_type == 1) + case DEMOD_TYPE_DRXK: return tuner_attach_tda18271(chan); + case DEMOD_TYPE_STV0367: + case DEMOD_TYPE_SONY_CT2: + case DEMOD_TYPE_SONY_ISDBT: + case DEMOD_TYPE_SONY_C2T2: + case DEMOD_TYPE_SONY_C2T2I: + return tuner_attach_tda18212(chan, chan->demod_type); + case DEMOD_TYPE_STV0910: + return tuner_attach_stv6111(chan); + } + return -EINVAL; } static int demod_attach_stv0900(struct ngene_channel *chan) { - struct i2c_adapter *i2c; + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); struct stv090x_config *feconf = (struct stv090x_config *) chan->dev->card_info->fe_config[chan->number]; @@ -144,7 +316,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan) (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 : STV090x_DEMODULATOR_1); if (chan->fe == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); + dev_err(pdev, "No STV0900 found!\n"); return -ENODEV; } @@ -154,7 +326,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan) if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, 0, chan->dev->card_info->lnb[chan->number])) { - printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); + dev_err(pdev, "No LNBH24 found!\n"); dvb_frontend_detach(chan->fe); chan->fe = NULL; return -ENODEV; @@ -163,6 +335,119 @@ static int demod_attach_stv0900(struct ngene_channel *chan) return 0; } +static struct stv0910_cfg stv0910_p = { + .adr = 0x68, + .parallel = 1, + .rptlvl = 4, + .clk = 30000000, +}; + +static struct lnbh25_config lnbh25_cfg = { + .i2c_address = 0x0c << 1, + .data2_config = LNBH25_TEN +}; + +static int demod_attach_stv0910(struct ngene_channel *chan, + struct i2c_adapter *i2c) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + struct stv0910_cfg cfg = stv0910_p; + struct lnbh25_config lnbcfg = lnbh25_cfg; + + chan->fe = dvb_attach(stv0910_attach, i2c, &cfg, (chan->number & 1)); + if (!chan->fe) { + cfg.adr = 0x6c; + chan->fe = dvb_attach(stv0910_attach, i2c, + &cfg, (chan->number & 1)); + } + if (!chan->fe) { + dev_err(pdev, "stv0910_attach() failed!\n"); + return -ENODEV; + } + + /* + * attach lnbh25 - leftshift by one as the lnbh25 driver expects 8bit + * i2c addresses + */ + lnbcfg.i2c_address = (((chan->number & 1) ? 0x0d : 0x0c) << 1); + if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) { + lnbcfg.i2c_address = (((chan->number & 1) ? 0x09 : 0x08) << 1); + if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) { + dev_err(pdev, "lnbh25_attach() failed!\n"); + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + return -ENODEV; + } + } + + return 0; +} + +static struct stv0367_config ddb_stv0367_config[] = { + { + .demod_address = 0x1f, + .xtal = 27000000, + .if_khz = 0, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, { + .demod_address = 0x1e, + .xtal = 27000000, + .if_khz = 0, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, +}; + +static int demod_attach_stv0367(struct ngene_channel *chan, + struct i2c_adapter *i2c) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + + chan->fe = dvb_attach(stv0367ddb_attach, + &ddb_stv0367_config[(chan->number & 1)], i2c); + + if (!chan->fe) { + dev_err(pdev, "stv0367ddb_attach() failed!\n"); + return -ENODEV; + } + + chan->fe->sec_priv = chan; + chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; + chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + return 0; +} + +static int demod_attach_cxd28xx(struct ngene_channel *chan, + struct i2c_adapter *i2c, int osc24) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + struct cxd2841er_config cfg; + + /* the cxd2841er driver expects 8bit/shifted I2C addresses */ + cfg.i2c_addr = ((chan->number & 1) ? 0x6d : 0x6c) << 1; + + cfg.xtal = osc24 ? SONY_XTAL_24000 : SONY_XTAL_20500; + cfg.flags = CXD2841ER_AUTO_IFHZ | CXD2841ER_EARLY_TUNE | + CXD2841ER_NO_WAIT_LOCK | CXD2841ER_NO_AGCNEG | + CXD2841ER_TSBITS | CXD2841ER_TS_SERIAL; + + /* attach frontend */ + chan->fe = dvb_attach(cxd2841er_attach_t_c, &cfg, i2c); + + if (!chan->fe) { + dev_err(pdev, "CXD28XX attach failed!\n"); + return -ENODEV; + } + + chan->fe->sec_priv = chan; + chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; + chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + return 0; +} + static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) { struct ngene_channel *chan = fe->analog_demod_priv; @@ -173,44 +458,61 @@ static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) up(&chan->dev->pll_mutex); } -static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +static int port_has_stv0900(struct i2c_adapter *i2c, int port) { - struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1 } }; - return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; + u8 val; + if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) + return 0; + return 1; } -static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, - u16 reg, u8 *val) +static int port_has_drxk(struct i2c_adapter *i2c, int port) { - u8 msg[2] = {reg>>8, reg&0xff}; - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = msg, .len = 2}, - {.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1} }; - return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; + u8 val; + + if (i2c_read(i2c, 0x29+port, &val) < 0) + return 0; + return 1; } -static int port_has_stv0900(struct i2c_adapter *i2c, int port) +static int port_has_stv0367(struct i2c_adapter *i2c) { u8 val; - if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) + + if (i2c_read_reg16(i2c, 0x1e, 0xf000, &val) < 0) + return 0; + if (val != 0x60) + return 0; + if (i2c_read_reg16(i2c, 0x1f, 0xf000, &val) < 0) + return 0; + if (val != 0x60) return 0; return 1; } -static int port_has_drxk(struct i2c_adapter *i2c, int port) +int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type) { u8 val; - - if (i2c_read(i2c, 0x29+port, &val) < 0) + u8 probe[4] = { 0xe0, 0x00, 0x00, 0x00 }, data[4]; + struct i2c_msg msgs[2] = {{ .addr = 0x40, .flags = 0, + .buf = probe, .len = 4 }, + { .addr = 0x40, .flags = I2C_M_RD, + .buf = data, .len = 4 } }; + val = i2c_transfer(i2c, msgs, 2); + if (val != 2) return 0; + + if (data[0] == 0x02 && data[1] == 0x2b && data[3] == 0x43) + *type = 2; + else + *type = 1; return 1; } static int demod_attach_drxk(struct ngene_channel *chan, struct i2c_adapter *i2c) { + struct device *pdev = &chan->dev->pci_dev->dev; struct drxk_config config; memset(&config, 0, sizeof(config)); @@ -220,7 +522,7 @@ static int demod_attach_drxk(struct ngene_channel *chan, chan->fe = dvb_attach(drxk_attach, &config, i2c); if (!chan->fe) { - printk(KERN_ERR "No DRXK found!\n"); + dev_err(pdev, "No DRXK found!\n"); return -ENODEV; } chan->fe->sec_priv = chan; @@ -229,22 +531,153 @@ static int demod_attach_drxk(struct ngene_channel *chan, return 0; } +/****************************************************************************/ +/* XO2 related lists and functions ******************************************/ +/****************************************************************************/ + +static char *xo2names[] = { + "DUAL DVB-S2", + "DUAL DVB-C/T/T2", + "DUAL DVB-ISDBT", + "DUAL DVB-C/C2/T/T2", + "DUAL ATSC", + "DUAL DVB-C/C2/T/T2/I", +}; + +static int init_xo2(struct ngene_channel *chan, struct i2c_adapter *i2c) +{ + struct device *pdev = &chan->dev->pci_dev->dev; + u8 addr = 0x10; + u8 val, data[2]; + int res; + + res = i2c_read_regs(i2c, addr, 0x04, data, 2); + if (res < 0) + return res; + + if (data[0] != 0x01) { + dev_info(pdev, "Invalid XO2 on channel %d\n", chan->number); + return -1; + } + + i2c_read_reg(i2c, addr, 0x08, &val); + if (val != 0) { + i2c_write_reg(i2c, addr, 0x08, 0x00); + msleep(100); + } + /* Enable tuner power, disable pll, reset demods */ + i2c_write_reg(i2c, addr, 0x08, 0x04); + usleep_range(2000, 3000); + /* Release demod resets */ + i2c_write_reg(i2c, addr, 0x08, 0x07); + + /* + * speed: 0=55,1=75,2=90,3=104 MBit/s + * Note: The ngene hardware must be run at 75 MBit/s compared + * to more modern ddbridge hardware which runs at 90 MBit/s, + * else there will be issues with the data transport and non- + * working secondary/slave demods/tuners. + */ + i2c_write_reg(i2c, addr, 0x09, 1); + + i2c_write_reg(i2c, addr, 0x0a, 0x01); + i2c_write_reg(i2c, addr, 0x0b, 0x01); + + usleep_range(2000, 3000); + /* Start XO2 PLL */ + i2c_write_reg(i2c, addr, 0x08, 0x87); + + return 0; +} + +static int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id) +{ + u8 probe[1] = { 0x00 }, data[4]; + u8 addr = 0x10; + + *type = NGENE_XO2_TYPE_NONE; + + if (i2c_io(i2c, addr, probe, 1, data, 4)) + return 0; + if (data[0] == 'D' && data[1] == 'F') { + *id = data[2]; + *type = NGENE_XO2_TYPE_DUOFLEX; + return 1; + } + if (data[0] == 'C' && data[1] == 'I') { + *id = data[2]; + *type = NGENE_XO2_TYPE_CI; + return 1; + } + return 0; +} + +/****************************************************************************/ +/* Probing and port/channel handling ****************************************/ +/****************************************************************************/ + static int cineS2_probe(struct ngene_channel *chan) { - struct i2c_adapter *i2c; + struct device *pdev = &chan->dev->pci_dev->dev; + struct i2c_adapter *i2c = i2c_adapter_from_chan(chan); struct stv090x_config *fe_conf; u8 buf[3]; + u8 xo2_type, xo2_id, xo2_demodtype; + u8 sony_osc24 = 0; struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; int rc; - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - - if (port_has_stv0900(i2c, chan->number)) { - chan->demod_type = 0; + if (port_has_xo2(i2c, &xo2_type, &xo2_id)) { + xo2_id >>= 2; + dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n", + chan->number, xo2_type, xo2_id); + + switch (xo2_type) { + case NGENE_XO2_TYPE_DUOFLEX: + if (chan->number & 1) + dev_dbg(pdev, + "skipping XO2 init on odd channel %d", + chan->number); + else + init_xo2(chan, i2c); + + xo2_demodtype = DEMOD_TYPE_XO2 + xo2_id; + + switch (xo2_demodtype) { + case DEMOD_TYPE_SONY_CT2: + case DEMOD_TYPE_SONY_ISDBT: + case DEMOD_TYPE_SONY_C2T2: + case DEMOD_TYPE_SONY_C2T2I: + dev_info(pdev, "%s (XO2) on channel %d\n", + xo2names[xo2_id], chan->number); + chan->demod_type = xo2_demodtype; + if (xo2_demodtype == DEMOD_TYPE_SONY_C2T2I) + sony_osc24 = 1; + + demod_attach_cxd28xx(chan, i2c, sony_osc24); + break; + case DEMOD_TYPE_STV0910: + dev_info(pdev, "%s (XO2) on channel %d\n", + xo2names[xo2_id], chan->number); + chan->demod_type = xo2_demodtype; + demod_attach_stv0910(chan, i2c); + break; + default: + dev_warn(pdev, + "Unsupported XO2 module on channel %d\n", + chan->number); + return -ENODEV; + } + break; + case NGENE_XO2_TYPE_CI: + dev_info(pdev, "DuoFlex CI modules not supported\n"); + return -ENODEV; + default: + dev_info(pdev, "Unsupported XO2 module type\n"); + return -ENODEV; + } + } else if (port_has_stv0900(i2c, chan->number)) { + chan->demod_type = DEMOD_TYPE_STV090X; fe_conf = chan->dev->card_info->fe_config[chan->number]; /* demod found, attach it */ rc = demod_attach_stv0900(chan); @@ -269,14 +702,18 @@ static int cineS2_probe(struct ngene_channel *chan) } rc = i2c_transfer(i2c, &i2c_msg, 1); if (rc != 1) { - printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); + dev_err(pdev, "Could not setup DPNx\n"); return -EIO; } } else if (port_has_drxk(i2c, chan->number^2)) { - chan->demod_type = 1; + chan->demod_type = DEMOD_TYPE_DRXK; demod_attach_drxk(chan, i2c); + } else if (port_has_stv0367(i2c)) { + chan->demod_type = DEMOD_TYPE_STV0367; + dev_info(pdev, "STV0367 on channel %d\n", chan->number); + demod_attach_stv0367(chan, i2c); } else { - printk(KERN_ERR "No demod found on chan %d\n", chan->number); + dev_info(pdev, "No demod found on chan %d\n", chan->number); return -ENODEV; } return 0; @@ -299,9 +736,11 @@ static struct mt2131_config m780_tunerconfig = { */ static int demod_attach_lg330x(struct ngene_channel *chan) { + struct device *pdev = &chan->dev->pci_dev->dev; + chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); if (chan->fe == NULL) { - printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); + dev_err(pdev, "No LGDT330x found!\n"); return -ENODEV; } @@ -313,6 +752,7 @@ static int demod_attach_lg330x(struct ngene_channel *chan) static int demod_attach_drxd(struct ngene_channel *chan) { + struct device *pdev = &chan->dev->pci_dev->dev; struct drxd_config *feconf; feconf = chan->dev->card_info->fe_config[chan->number]; @@ -320,7 +760,7 @@ static int demod_attach_drxd(struct ngene_channel *chan) chan->fe = dvb_attach(drxd_attach, feconf, chan, &chan->i2c_adapter, &chan->dev->pci_dev->dev); if (!chan->fe) { - pr_err("No DRXD found!\n"); + dev_err(pdev, "No DRXD found!\n"); return -ENODEV; } return 0; @@ -328,6 +768,7 @@ static int demod_attach_drxd(struct ngene_channel *chan) static int tuner_attach_dtt7520x(struct ngene_channel *chan) { + struct device *pdev = &chan->dev->pci_dev->dev; struct drxd_config *feconf; feconf = chan->dev->card_info->fe_config[chan->number]; @@ -335,7 +776,7 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan) if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, &chan->i2c_adapter, feconf->pll_type)) { - pr_err("No pll(%d) found!\n", feconf->pll_type); + dev_err(pdev, "No pll(%d) found!\n", feconf->pll_type); return -ENODEV; } return 0; @@ -371,12 +812,13 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan) static int i2c_write_eeprom(struct i2c_adapter *adapter, u8 adr, u16 reg, u8 data) { + struct device *pdev = adapter->dev.parent; u8 m[3] = {(reg >> 8), (reg & 0xff), data}; struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = sizeof(m)}; if (i2c_transfer(adapter, &msg, 1) != 1) { - pr_err(DEVICE_NAME ": Error writing EEPROM!\n"); + dev_err(pdev, "Error writing EEPROM!\n"); return -EIO; } return 0; @@ -385,6 +827,7 @@ static int i2c_write_eeprom(struct i2c_adapter *adapter, static int i2c_read_eeprom(struct i2c_adapter *adapter, u8 adr, u16 reg, u8 *data, int len) { + struct device *pdev = adapter->dev.parent; u8 msg[2] = {(reg >> 8), (reg & 0xff)}; struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, .buf = msg, .len = 2 }, @@ -392,7 +835,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter, .buf = data, .len = len} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - pr_err(DEVICE_NAME ": Error reading EEPROM\n"); + dev_err(pdev, "Error reading EEPROM\n"); return -EIO; } return 0; @@ -401,6 +844,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter, static int ReadEEProm(struct i2c_adapter *adapter, u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) { + struct device *pdev = adapter->dev.parent; int status = 0; u16 Addr = MICNG_EE_START, Length, tag = 0; u8 EETag[3]; @@ -416,9 +860,8 @@ static int ReadEEProm(struct i2c_adapter *adapter, Addr += sizeof(u16) + 1 + EETag[2]; } if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - pr_err(DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); + dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); return -1; } Length = EETag[2]; @@ -441,6 +884,7 @@ static int ReadEEProm(struct i2c_adapter *adapter, static int WriteEEProm(struct i2c_adapter *adapter, u16 Tag, u32 Length, u8 *data) { + struct device *pdev = adapter->dev.parent; int status = 0; u16 Addr = MICNG_EE_START; u8 EETag[3]; @@ -458,9 +902,8 @@ static int WriteEEProm(struct i2c_adapter *adapter, Addr += sizeof(u16) + 1 + EETag[2]; } if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - pr_err(DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); + dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); return -1; } @@ -487,13 +930,11 @@ static int WriteEEProm(struct i2c_adapter *adapter, if (status) break; if (Tmp != data[i]) - pr_err(DEVICE_NAME - "eeprom write error\n"); + dev_err(pdev, "eeprom write error\n"); retry -= 1; } if (status) { - pr_err(DEVICE_NAME - ": Timeout polling eeprom\n"); + dev_err(pdev, "Timeout polling eeprom\n"); break; } } @@ -532,19 +973,20 @@ static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) static s16 osc_deviation(void *priv, s16 deviation, int flag) { struct ngene_channel *chan = priv; + struct device *pdev = &chan->dev->pci_dev->dev; struct i2c_adapter *adap = &chan->i2c_adapter; u16 data = 0; if (flag) { data = (u16) deviation; - pr_info(DEVICE_NAME ": write deviation %d\n", - deviation); + dev_info(pdev, "write deviation %d\n", + deviation); eeprom_write_ushort(adap, 0x1000 + chan->number, data); } else { if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) data = 0; - pr_info(DEVICE_NAME ": read deviation %d\n", - (s16) data); + dev_info(pdev, "read deviation %d\n", + (s16)data); } return (s16) data; @@ -749,6 +1191,8 @@ static const struct ngene_info ngene_info_terratec = { /****************************************************************************/ static const struct pci_device_id ngene_id_tbl[] = { + NGENE_ID(0x18c3, 0xab04, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xab05, ngene_info_cineS2v5), NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), @@ -769,7 +1213,7 @@ MODULE_DEVICE_TABLE(pci, ngene_id_tbl); static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, enum pci_channel_state state) { - printk(KERN_ERR DEVICE_NAME ": PCI error\n"); + dev_err(&dev->dev, "PCI error\n"); if (state == pci_channel_io_perm_failure) return PCI_ERS_RESULT_DISCONNECT; if (state == pci_channel_io_frozen) @@ -779,13 +1223,13 @@ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) { - printk(KERN_INFO DEVICE_NAME ": slot reset\n"); + dev_info(&dev->dev, "slot reset\n"); return 0; } static void ngene_resume(struct pci_dev *dev) { - printk(KERN_INFO DEVICE_NAME ": resume\n"); + dev_info(&dev->dev, "resume\n"); } static const struct pci_error_handlers ngene_errors = { @@ -805,8 +1249,9 @@ static struct pci_driver ngene_pci_driver = { static __init int module_init_ngene(void) { - printk(KERN_INFO - "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); + /* pr_*() since we don't have a device to use with dev_*() yet */ + pr_info("nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); + return pci_register_driver(&ngene_pci_driver); } diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 8c92cb7f7e72..25f16833a475 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -51,8 +51,6 @@ MODULE_PARM_DESC(debug, "Print debugging information."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define dprintk if (debug) printk - #define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) #define ngwritel(dat, adr) writel((dat), dev->iomem + (adr)) #define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) @@ -86,6 +84,7 @@ static void event_tasklet(unsigned long data) static void demux_tasklet(unsigned long data) { struct ngene_channel *chan = (struct ngene_channel *)data; + struct device *pdev = &chan->dev->pci_dev->dev; struct SBufferHeader *Cur = chan->nextBuffer; spin_lock_irq(&chan->state_lock); @@ -124,16 +123,15 @@ static void demux_tasklet(unsigned long data) chan->HWState = HWSTATE_RUN; } } else { - printk(KERN_ERR DEVICE_NAME ": OOPS\n"); + dev_err(pdev, "OOPS\n"); if (chan->HWState == HWSTATE_RUN) { Cur->ngeneBuffer.SR.Flags &= ~0x40; break; /* Stop processing stream */ } } if (chan->AudioDTOUpdated) { - printk(KERN_INFO DEVICE_NAME - ": Update AudioDTO = %d\n", - chan->AudioDTOValue); + dev_info(pdev, "Update AudioDTO = %d\n", + chan->AudioDTOValue); Cur->ngeneBuffer.SR.DTOUpdate = chan->AudioDTOValue; chan->AudioDTOUpdated = 0; @@ -173,6 +171,7 @@ static void demux_tasklet(unsigned long data) static irqreturn_t irq_handler(int irq, void *dev_id) { struct ngene *dev = (struct ngene *)dev_id; + struct device *pdev = &dev->pci_dev->dev; u32 icounts = 0; irqreturn_t rc = IRQ_NONE; u32 i = MAX_STREAM; @@ -213,7 +212,7 @@ static irqreturn_t irq_handler(int irq, void *dev_id) *(dev->EventBuffer); dev->EventQueueWriteIndex = nextWriteIndex; } else { - printk(KERN_ERR DEVICE_NAME ": event overflow\n"); + dev_err(pdev, "event overflow\n"); dev->EventQueueOverflowCount += 1; dev->EventQueueOverflowFlag = 1; } @@ -249,23 +248,25 @@ static irqreturn_t irq_handler(int irq, void *dev_id) static void dump_command_io(struct ngene *dev) { + struct device *pdev = &dev->pci_dev->dev; u8 buf[8], *b; ngcpyfrom(buf, HOST_TO_NGENE, 8); - printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf); + dev_err(pdev, "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf); ngcpyfrom(buf, NGENE_TO_HOST, 8); - printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf); + dev_err(pdev, "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf); b = dev->hosttongene; - printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b); + dev_err(pdev, "dev->hosttongene (%p): %*ph\n", b, 8, b); b = dev->ngenetohost; - printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b); + dev_err(pdev, "dev->ngenetohost (%p): %*ph\n", b, 8, b); } static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) { + struct device *pdev = &dev->pci_dev->dev; int ret; u8 *tmpCmdDoneByte; @@ -313,9 +314,8 @@ static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) if (!ret) { /*ngwritel(0, FORCE_NMI);*/ - printk(KERN_ERR DEVICE_NAME - ": Command timeout cmd=%02x prev=%02x\n", - com->cmd.hdr.Opcode, dev->prev_cmd); + dev_err(pdev, "Command timeout cmd=%02x prev=%02x\n", + com->cmd.hdr.Opcode, dev->prev_cmd); dump_command_io(dev); return -1; } @@ -553,6 +553,7 @@ static void clear_buffers(struct ngene_channel *chan) static int ngene_command_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, u8 flags) { + struct device *pdev = &dev->pci_dev->dev; struct ngene_channel *chan = &dev->channel[stream]; struct ngene_command com; u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); @@ -572,8 +573,7 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, com.in_len = sizeof(struct FW_STREAM_CONTROL); com.out_len = 0; - dprintk(KERN_INFO DEVICE_NAME - ": Stream=%02x, Control=%02x, Mode=%02x\n", + dev_dbg(pdev, "Stream=%02x, Control=%02x, Mode=%02x\n", com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, com.cmd.StreamControl.Mode); @@ -695,23 +695,24 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, void set_transfer(struct ngene_channel *chan, int state) { + struct device *pdev = &chan->dev->pci_dev->dev; u8 control = 0, mode = 0, flags = 0; struct ngene *dev = chan->dev; int ret; /* - printk(KERN_INFO DEVICE_NAME ": st %d\n", state); + dev_info(pdev, "st %d\n", state); msleep(100); */ if (state) { if (chan->running) { - printk(KERN_INFO DEVICE_NAME ": already running\n"); + dev_info(pdev, "already running\n"); return; } } else { if (!chan->running) { - printk(KERN_INFO DEVICE_NAME ": already stopped\n"); + dev_info(pdev, "already stopped\n"); return; } } @@ -722,7 +723,7 @@ void set_transfer(struct ngene_channel *chan, int state) if (state) { spin_lock_irq(&chan->state_lock); - /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + /* dev_info(pdev, "lock=%08x\n", ngreadl(0x9310)); */ dvb_ringbuffer_flush(&dev->tsout_rbuf); control = 0x80; @@ -740,7 +741,7 @@ void set_transfer(struct ngene_channel *chan, int state) chan->pBufferExchange = tsin_exchange; spin_unlock_irq(&chan->state_lock); } - /* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + /* else dev_info(pdev, "lock=%08x\n", ngreadl(0x9310)); */ mutex_lock(&dev->stream_mutex); @@ -751,8 +752,7 @@ void set_transfer(struct ngene_channel *chan, int state) if (!ret) chan->running = state; else - printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", - state); + dev_err(pdev, "%s %d failed\n", __func__, state); if (!state) { spin_lock_irq(&chan->state_lock); chan->pBufferExchange = NULL; @@ -1195,6 +1195,7 @@ static int ngene_get_buffers(struct ngene *dev) static void ngene_init(struct ngene *dev) { + struct device *pdev = &dev->pci_dev->dev; int i; tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); @@ -1214,12 +1215,12 @@ static void ngene_init(struct ngene *dev) dev->icounts = ngreadl(NGENE_INT_COUNTS); dev->device_version = ngreadl(DEV_VER) & 0x0f; - printk(KERN_INFO DEVICE_NAME ": Device version %d\n", - dev->device_version); + dev_info(pdev, "Device version %d\n", dev->device_version); } static int ngene_load_firm(struct ngene *dev) { + struct device *pdev = &dev->pci_dev->dev; u32 size; const struct firmware *fw = NULL; u8 *ngene_fw; @@ -1253,21 +1254,18 @@ static int ngene_load_firm(struct ngene *dev) } if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { - printk(KERN_ERR DEVICE_NAME - ": Could not load firmware file %s.\n", fw_name); - printk(KERN_INFO DEVICE_NAME - ": Copy %s to your hotplug directory!\n", fw_name); + dev_err(pdev, "Could not load firmware file %s.\n", fw_name); + dev_info(pdev, "Copy %s to your hotplug directory!\n", + fw_name); return -1; } if (size == 0) size = fw->size; if (size != fw->size) { - printk(KERN_ERR DEVICE_NAME - ": Firmware %s has invalid size!", fw_name); + dev_err(pdev, "Firmware %s has invalid size!", fw_name); err = -1; } else { - printk(KERN_INFO DEVICE_NAME - ": Loading firmware file %s.\n", fw_name); + dev_info(pdev, "Loading firmware file %s.\n", fw_name); ngene_fw = (u8 *) fw->data; err = ngene_command_load_firmware(dev, ngene_fw, size); } @@ -1360,14 +1358,14 @@ static int ngene_start(struct ngene *dev) #ifdef CONFIG_PCI_MSI /* enable MSI if kernel and card support it */ if (pci_msi_enabled() && dev->card_info->msi_supported) { + struct device *pdev = &dev->pci_dev->dev; unsigned long flags; ngwritel(0, NGENE_INT_ENABLE); free_irq(dev->pci_dev->irq, dev); stat = pci_enable_msi(dev->pci_dev); if (stat) { - printk(KERN_INFO DEVICE_NAME - ": MSI not available\n"); + dev_info(pdev, "MSI not available\n"); flags = IRQF_SHARED; } else { flags = 0; @@ -1426,6 +1424,13 @@ static void release_channel(struct ngene_channel *chan) if (chan->fe) { dvb_unregister_frontend(chan->fe); + + /* release I2C client (tuner) if needed */ + if (chan->i2c_client_fe) { + dvb_module_release(chan->i2c_client[0]); + chan->i2c_client[0] = NULL; + } + dvb_frontend_detach(chan->fe); chan->fe = NULL; } @@ -1461,6 +1466,7 @@ static int init_channel(struct ngene_channel *chan) chan->users = 0; chan->type = io; chan->mode = chan->type; /* for now only one mode */ + chan->i2c_client_fe = 0; /* be sure this is set to zero */ if (io & NGENE_IO_TSIN) { chan->fe = NULL; @@ -1562,19 +1568,46 @@ static int init_channels(struct ngene *dev) return 0; } -static struct cxd2099_cfg cxd_cfg = { +static const struct cxd2099_cfg cxd_cfgtmpl = { .bitrate = 62000, - .adr = 0x40, .polarity = 0, .clock_mode = 0, }; static void cxd_attach(struct ngene *dev) { + struct device *pdev = &dev->pci_dev->dev; struct ngene_ci *ci = &dev->ci; + struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl; + struct i2c_client *client; + int ret; + u8 type; + + /* check for CXD2099AR presence before attaching */ + ret = ngene_port_has_cxd2099(&dev->channel[0].i2c_adapter, &type); + if (!ret) { + dev_dbg(pdev, "No CXD2099AR found\n"); + return; + } + + if (type != 1) { + dev_warn(pdev, "CXD2099AR is uninitialized!\n"); + return; + } + + cxd_cfg.en = &ci->en; + client = dvb_module_probe("cxd2099", NULL, + &dev->channel[0].i2c_adapter, + 0x40, &cxd_cfg); + if (!client) + goto err; - ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter); ci->dev = dev; + dev->channel[0].i2c_client[0] = client; + return; + +err: + dev_err(pdev, "CXD2099AR attach failed\n"); return; } @@ -1583,7 +1616,9 @@ static void cxd_detach(struct ngene *dev) struct ngene_ci *ci = &dev->ci; dvb_ca_en50221_release(ci->en); - kfree(ci->en); + + dvb_module_release(dev->channel[0].i2c_client[0]); + dev->channel[0].i2c_client[0] = NULL; ci->en = NULL; } @@ -1615,7 +1650,7 @@ void ngene_shutdown(struct pci_dev *pdev) if (!dev || !shutdown_workaround) return; - printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); + dev_info(&pdev->dev, "shutdown workaround...\n"); ngene_unlink(dev); pci_disable_device(pdev); } @@ -1655,7 +1690,7 @@ int ngene_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) dev->pci_dev = pci_dev; dev->card_info = (struct ngene_info *)id->driver_data; - printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); + dev_info(&pci_dev->dev, "Found %s\n", dev->card_info->name); pci_set_drvdata(pci_dev, dev); diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c index 03fc218a45e9..fee89b9ed9c1 100644 --- a/drivers/media/pci/ngene/ngene-dvb.c +++ b/drivers/media/pci/ngene/ngene-dvb.c @@ -38,6 +38,9 @@ #include "ngene.h" +static int ci_tsfix = 1; +module_param(ci_tsfix, int, 0444); +MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifs in conjunction with CI expansions (default: 1/enabled)"); /****************************************************************************/ /* COMMAND API interface ****************************************************/ @@ -84,18 +87,41 @@ static ssize_t ts_read(struct file *file, char __user *buf, return count; } +static __poll_t ts_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + struct dvb_ringbuffer *rbuf = &dev->tsin_rbuf; + struct dvb_ringbuffer *wbuf = &dev->tsout_rbuf; + __poll_t mask = 0; + + poll_wait(file, &rbuf->queue, wait); + poll_wait(file, &wbuf->queue, wait); + + if (!dvb_ringbuffer_empty(rbuf)) + mask |= EPOLLIN | EPOLLRDNORM; + if (dvb_ringbuffer_free(wbuf) >= 188) + mask |= EPOLLOUT | EPOLLWRNORM; + + return mask; +} + static const struct file_operations ci_fops = { .owner = THIS_MODULE, .read = ts_read, .write = ts_write, .open = dvb_generic_open, .release = dvb_generic_release, + .poll = ts_poll, + .mmap = NULL, }; struct dvb_device ngene_dvbdev_ci = { - .readers = -1, - .writers = -1, - .users = -1, + .priv = NULL, + .readers = 1, + .writers = 1, + .users = 2, .fops = &ci_fops, }; @@ -116,47 +142,118 @@ static void swap_buffer(u32 *p, u32 len) /* start of filler packet */ static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER }; -/* #define DEBUG_CI_XFER */ -#ifdef DEBUG_CI_XFER -static u32 ok; -static u32 overflow; -static u32 stripped; -#endif +static int tsin_find_offset(void *buf, u32 len) +{ + int i, l; + + l = len - sizeof(fill_ts); + if (l <= 0) + return -1; + + for (i = 0; i < l; i++) { + if (((char *)buf)[i] == 0x47) { + if (!memcmp(buf + i, fill_ts, sizeof(fill_ts))) + return i % 188; + } + } + + return -1; +} + +static inline void tsin_copy_stripped(struct ngene *dev, void *buf) +{ + if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) { + if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { + dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); + wake_up(&dev->tsin_rbuf.queue); + } + } +} void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) { struct ngene_channel *chan = priv; struct ngene *dev = chan->dev; - + int tsoff; if (flags & DF_SWAP32) swap_buffer(buf, len); if (dev->ci.en && chan->number == 2) { + /* blindly copy buffers if ci_tsfix is disabled */ + if (!ci_tsfix) { + while (len >= 188) { + tsin_copy_stripped(dev, buf); + + buf += 188; + len -= 188; + } + return NULL; + } + + /* ci_tsfix = 1 */ + + /* + * since the remainder of the TS packet which got cut off + * in the previous tsin_exchange() run is at the beginning + * of the new TS buffer, append this to the temp buffer and + * send it to the DVB ringbuffer afterwards. + */ + if (chan->tsin_offset) { + memcpy(&chan->tsin_buffer[(188 - chan->tsin_offset)], + buf, chan->tsin_offset); + tsin_copy_stripped(dev, &chan->tsin_buffer); + + buf += chan->tsin_offset; + len -= chan->tsin_offset; + } + + /* + * copy TS packets to the DVB ringbuffer and detect new offset + * shifts by checking for a valid TS SYNC byte + */ while (len >= 188) { - if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) { - if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { - dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); - wake_up(&dev->tsin_rbuf.queue); -#ifdef DEBUG_CI_XFER - ok++; -#endif + if (*((char *)buf) != 0x47) { + /* + * no SYNC header, find new offset shift + * (max. 188 bytes, tsoff will be mod 188) + */ + tsoff = tsin_find_offset(buf, len); + if (tsoff > 0) { + chan->tsin_offset += tsoff; + chan->tsin_offset %= 188; + + buf += tsoff; + len -= tsoff; + + dev_info(&dev->pci_dev->dev, + "%s(): tsin_offset shift by %d on channel %d\n", + __func__, tsoff, + chan->number); + + /* + * offset corrected. re-check remaining + * len for a full TS frame, break and + * skip to fragment handling if < 188. + */ + if (len < 188) + break; } -#ifdef DEBUG_CI_XFER - else - overflow++; -#endif } -#ifdef DEBUG_CI_XFER - else - stripped++; - if (ok % 100 == 0 && overflow) - printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped); -#endif + tsin_copy_stripped(dev, buf); + buf += 188; len -= 188; } + + /* + * if a fragment is left, copy to temp buffer. The remainder + * will be appended in the next tsin_exchange() iteration. + */ + if (len > 0 && len < 188) + memcpy(&chan->tsin_buffer, buf, len); + return NULL; } diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c index 3004947f300b..092d46c2a3a9 100644 --- a/drivers/media/pci/ngene/ngene-i2c.c +++ b/drivers/media/pci/ngene/ngene-i2c.c @@ -147,7 +147,7 @@ done: static u32 ngene_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm ngene_i2c_algo = { diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index 02dbd18f92d0..01d9f1b58fcb 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -51,6 +51,22 @@ #define VIDEO_CAP_MPEG4 512 #endif +#define DEMOD_TYPE_STV090X 0 +#define DEMOD_TYPE_DRXK 1 +#define DEMOD_TYPE_STV0367 2 + +#define DEMOD_TYPE_XO2 32 +#define DEMOD_TYPE_STV0910 (DEMOD_TYPE_XO2 + 0) +#define DEMOD_TYPE_SONY_CT2 (DEMOD_TYPE_XO2 + 1) +#define DEMOD_TYPE_SONY_ISDBT (DEMOD_TYPE_XO2 + 2) +#define DEMOD_TYPE_SONY_C2T2 (DEMOD_TYPE_XO2 + 3) +#define DEMOD_TYPE_ST_ATSC (DEMOD_TYPE_XO2 + 4) +#define DEMOD_TYPE_SONY_C2T2I (DEMOD_TYPE_XO2 + 5) + +#define NGENE_XO2_TYPE_NONE 0 +#define NGENE_XO2_TYPE_DUOFLEX 1 +#define NGENE_XO2_TYPE_CI 2 + enum STREAM { STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ STREAM_VIDEOIN2, @@ -630,6 +646,8 @@ struct ngene_vopen { struct ngene_channel { struct device device; struct i2c_adapter i2c_adapter; + struct i2c_client *i2c_client[1]; + int i2c_client_fe; struct ngene *dev; int number; @@ -714,6 +732,9 @@ struct ngene_channel { #endif int running; + + int tsin_offset; + u8 tsin_buffer[188]; }; @@ -891,6 +912,9 @@ int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); void set_transfer(struct ngene_channel *chan, int state); void FillTSBuffer(void *Buffer, int Length, u32 Flags); +/* Provided by ngene-cards.c */ +int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type); + /* Provided by ngene-i2c.c */ int ngene_i2c_init(struct ngene *dev, int dev_nr); diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index c59b69f1af9d..72311445d13d 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -273,9 +273,8 @@ static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) return -ENOMEM; } - pr_debug("vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)dma->vaddr, - nr_pages << PAGE_SHIFT); + pr_debug("vmalloc is at addr %p, size=%d\n", + dma->vaddr, nr_pages << PAGE_SHIFT); memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index a7a63d608dde..3025d38ddb2b 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -1195,7 +1195,7 @@ static struct s5h1411_config kworld_s5h1411_config = { .inversion = S5H1411_INVERSION_ON, .status_mode = S5H1411_DEMODLOCKING, .mpeg_timing = - S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 33ee8322895e..0e28c5021ac4 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -115,7 +115,7 @@ static int build_key(struct saa7134_dev *dev) static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { - int gpio; + int gpio, rc; int attempt = 0; unsigned char b; @@ -153,8 +153,11 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_proto *protocol, attempt); return -EIO; } - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -169,7 +172,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *scancode, u8 *toggle) { unsigned char b; - int gpio; + int gpio, rc; /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ struct saa7134_dev *dev = ir->c->adapter->algo_data; @@ -193,8 +196,11 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, /* GPIO says there is a button press. Get it. */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -218,6 +224,7 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol, { unsigned char b; unsigned int gpio; + int rc; /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ struct saa7134_dev *dev = ir->c->adapter->algo_data; @@ -241,8 +248,11 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol, /* GPIO says there is a button press. Get it. */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -263,11 +273,15 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_purpletv(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { + rc = i2c_master_recv(ir->c, &b, 1); + if (rc != 1) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -288,11 +302,17 @@ static int get_key_purpletv(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_hvr1110(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char buf[5]; /* poll IR chip */ - if (5 != i2c_master_recv(ir->c, buf, 5)) + rc = i2c_master_recv(ir->c, buf, 5); + if (rc != 5) { + ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; + } /* Check if some key were pressed */ if (!(buf[0] & 0x80)) @@ -319,6 +339,7 @@ static int get_key_hvr1110(struct IR_i2c *ir, enum rc_proto *protocol, static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle) { + int rc; unsigned char data[12]; u32 gpio; @@ -335,8 +356,11 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_proto *protocol, ir->c->addr = 0x5a >> 1; - if (12 != i2c_master_recv(ir->c, data, 12)) { + rc = i2c_master_recv(ir->c, data, 12); + if (rc != 12) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } @@ -356,12 +380,16 @@ static int get_key_pinnacle(struct IR_i2c *ir, enum rc_proto *protocol, u32 *scancode, u8 *toggle, int parity_offset, int marker, int code_modulo) { + int rc; unsigned char b[4]; unsigned int start = 0,parity = 0,code = 0; /* poll IR chip */ - if (4 != i2c_master_recv(ir->c, b, 4)) { + rc = i2c_master_recv(ir->c, b, 4); + if (rc != 4) { ir_dbg(ir, "read error\n"); + if (rc < 0) + return rc; return -EIO; } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 1ca6a32ad10e..1a50ec9d084f 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1200,7 +1200,7 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa_call_all(dev, core, s_power, 0); + saa_call_all(dev, tuner, standby); if (vdev->vfl_type == VFL_TYPE_RADIO) saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd); mutex_unlock(&dev->lock); @@ -1235,12 +1235,12 @@ static __poll_t radio_poll(struct file *file, poll_table *wait) cmd.instance = file; cmd.event_list = wait; - cmd.result = 0; + cmd.poll_mask = 0; mutex_lock(&dev->lock); saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); mutex_unlock(&dev->lock); - return rc | cmd.result; + return rc | cmd.poll_mask; } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c index e76d3bafe2ce..4f9f03c3b252 100644 --- a/drivers/media/pci/saa7164/saa7164-dvb.c +++ b/drivers/media/pci/saa7164/saa7164-dvb.c @@ -78,7 +78,7 @@ static struct s5h1411_config hauppauge_s5h1411_config = { .vsb_if = S5H1411_IF_3250, .inversion = S5H1411_INVERSION_ON, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct lgdt3306a_config hauppauge_hvr2255a_config = { diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 81be1b8df758..2ac33b5cc454 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) return idx * G723_FRAMES_PER_PAGE; } -static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss, - unsigned long pos, void *dst, - unsigned long count, bool in_kernel) +static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel, + unsigned long pos, void __user *dst, + unsigned long count) { struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); struct solo_dev *solo_dev = solo_pcm->solo_dev; @@ -242,10 +242,7 @@ static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss, if (err) return err; - if (in_kernel) - memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES); - else if (copy_to_user((void __user *)dst, - solo_pcm->g723_buf, G723_PERIOD_BYTES)) + if (copy_to_user(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES)) return -EFAULT; dst += G723_PERIOD_BYTES; } @@ -253,18 +250,30 @@ static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss, return 0; } -static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel, - unsigned long pos, void __user *dst, - unsigned long count) -{ - return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false); -} - static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel, unsigned long pos, void *dst, unsigned long count) { - return __snd_solo_pcm_copy(ss, pos, dst, count, true); + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int err, i; + + for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { + int page = (pos / G723_FRAMES_PER_PAGE) + i; + + err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES, 0, 0); + if (err) + return err; + + memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES); + dst += G723_PERIOD_BYTES; + } + + return 0; } static const struct snd_pcm_ops snd_solo_pcm_ops = { diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index 8c8484674d2f..46c30430e30b 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -69,14 +69,11 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev, unsigned int timeout; unsigned int config = 0; int ret = 0; - int p2m_id = 0; + unsigned int p2m_id = 0; /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ - if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { + if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; - if (p2m_id < 0) - p2m_id = -p2m_id; - } p2m_dev = &solo_dev->p2m_dev[p2m_id]; diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index dc8e577b2f74..d6816effb878 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -324,14 +324,15 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, buffer2, buffer2_len, - &dvbdmxfilter->filter); + &dvbdmxfilter->filter, NULL); case DMX_TYPE_TS: if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) return 0; if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, buffer2, buffer2_len, - &dvbdmxfilter->feed->feed.ts); + &dvbdmxfilter->feed->feed.ts, + NULL); else av7110_p2t_write(buffer1, buffer1_len, dvbdmxfilter->feed->pid, diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 4daba76ec240..ef1bc17cdc4d 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -99,7 +99,7 @@ int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) buf[4] = buf[5] = 0; if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfeed->cb.ts(buf, len, NULL, 0, - &dvbdmxfeed->feed.ts); + &dvbdmxfeed->feed.ts, NULL); else return dvb_filter_pes2ts(p2t, buf, len, 1); } @@ -109,7 +109,7 @@ static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; dvbdmxfeed->cb.ts(data, 188, NULL, 0, - &dvbdmxfeed->feed.ts); + &dvbdmxfeed->feed.ts, NULL); return 0; } @@ -814,7 +814,7 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, memcpy(obuf + l, buf + c, TS_SIZE - l); c = length; } - feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts); + feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, NULL); pes_start = 0; } } diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c index 9534f29c1ffd..78c7a6589be5 100644 --- a/drivers/media/pci/ttpci/ttpci-eeprom.c +++ b/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -138,7 +138,7 @@ static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encode int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) { - int ret, i; + int ret; u8 encodedMAC[20]; u8 decodedMAC[6]; @@ -153,11 +153,8 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) ret = getmac_tt(decodedMAC, encodedMAC); if( ret != 0 ) { dprintk("adapter failed MAC signature check\n"); - dprintk("encoded MAC from EEPROM was " ); - for(i=0; i<19; i++) { - dprintk( "%.2x:", encodedMAC[i]); - } - dprintk("%.2x\n", encodedMAC[19]); + dprintk("encoded MAC from EEPROM was %*phC", + (int)sizeof(encodedMAC), &encodedMAC); eth_zero_addr(proposed_mac); return ret; } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 8d4e7d930a66..14f9c0e26a1c 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -241,8 +241,8 @@ static int v4l_fbuffer_alloc(struct zoran_fh *fh) SetPageReserved(virt_to_page(mem + off)); dprintk(4, KERN_INFO - "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n", - ZR_DEVNAME(zr), __func__, i, (unsigned long) mem, + "%s: %s - V4L frame %d mem %p (bus: 0x%llx)\n", + ZR_DEVNAME(zr), __func__, i, mem, (unsigned long long)virt_to_bus(mem)); } diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 614fbef08ddc..c7a1cf8a1b01 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -31,8 +31,6 @@ source "drivers/media/platform/davinci/Kconfig" source "drivers/media/platform/omap/Kconfig" -source "drivers/media/platform/blackfin/Kconfig" - config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT @@ -54,26 +52,6 @@ config VIDEO_VIU Say Y here if you want to enable VIU device on MPC5121e Rev2+. In doubt, say N. -config VIDEO_M32R_AR - tristate "AR devices" - depends on VIDEO_V4L2 - depends on M32R || COMPILE_TEST - ---help--- - This is a video4linux driver for the Renesas AR (Artificial Retina) - camera module. - -config VIDEO_M32R_AR_M64278 - tristate "AR device with color module M64278(VGA)" - depends on PLAT_M32700UT - select VIDEO_M32R_AR - ---help--- - This is a video4linux driver for the Renesas AR (Artificial - Retina) with M64278E-800 camera module. - This module supports VGA(640x480 pixels) resolutions. - - To compile this driver as a module, choose M here: the - module will be called arv. - config VIDEO_MUX tristate "Video Multiplexer" select MULTIPLEXER @@ -144,6 +122,15 @@ config VIDEO_STM32_DCMI To compile this driver as a module, choose M here: the module will be called stm32-dcmi. +config VIDEO_RENESAS_CEU + tristate "Renesas Capture Engine Unit (CEU) driver" + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + ---help--- + This is a v4l2 driver for the Renesas CEU Interface + source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/am437x/Kconfig" @@ -582,7 +569,7 @@ config CEC_GPIO config VIDEO_SAMSUNG_S5P_CEC tristate "Samsung S5P CEC driver" - depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST + depends on ARCH_EXYNOS || COMPILE_TEST select CEC_CORE select CEC_NOTIFIER ---help--- diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 7f3080437be6..932515df4477 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,8 +3,6 @@ # Makefile for the video capture/playback device drivers. # -obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o - obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ @@ -53,8 +51,6 @@ obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC) += tegra-cec/ obj-y += stm32/ -obj-y += blackfin/ - obj-y += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o @@ -62,6 +58,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o +obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c deleted file mode 100644 index 1e865fea803c..000000000000 --- a/drivers/media/platform/arv.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - * Colour AR M64278(VGA) driver for Video4Linux - * - * Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.com> - * - * 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. - * - * Some code is taken from AR driver sample program for M3T-M32700UT. - * - * AR driver sample (M32R SDK): - * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION - * AND RENESAS SOLUTIONS CORPORATION - * All Rights Reserved. - * - * 2003-09-01: Support w3cam by Takeo Takahashi - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fh.h> -#include <linux/mutex.h> - -#include <linux/uaccess.h> -#include <asm/m32r.h> -#include <asm/io.h> -#include <asm/dma.h> -#include <asm/byteorder.h> - -#if 0 -#define DEBUG(n, args...) printk(KERN_INFO args) -#define CHECK_LOST 1 -#else -#define DEBUG(n, args...) -#define CHECK_LOST 0 -#endif - -/* - * USE_INT is always 0, interrupt mode is not available - * on linux due to lack of speed - */ -#define USE_INT 0 /* Don't modify */ - -#define VERSION "0.0.5" - -#define ar_inl(addr) inl((unsigned long)(addr)) -#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr)) - -extern struct cpuinfo_m32r boot_cpu_data; - -/* - * CCD pixel size - * Note that M32700UT does not support CIF mode, but QVGA is - * supported by M32700UT hardware using VGA mode of AR LSI. - * - * Supported: VGA (Normal mode, Interlace mode) - * QVGA (Always Interlace mode of VGA) - * - */ -#define AR_WIDTH_VGA 640 -#define AR_HEIGHT_VGA 480 -#define AR_WIDTH_QVGA 320 -#define AR_HEIGHT_QVGA 240 -#define MIN_AR_WIDTH AR_WIDTH_QVGA -#define MIN_AR_HEIGHT AR_HEIGHT_QVGA -#define MAX_AR_WIDTH AR_WIDTH_VGA -#define MAX_AR_HEIGHT AR_HEIGHT_VGA - -/* bits & bytes per pixel */ -#define AR_BITS_PER_PIXEL 16 -#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8) - -/* line buffer size */ -#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL) -#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL) -#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA - -/* frame size & type */ -#define AR_FRAME_BYTES_VGA \ - (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL) -#define AR_FRAME_BYTES_QVGA \ - (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL) -#define MAX_AR_FRAME_BYTES \ - (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL) - -#define AR_MAX_FRAME 15 - -/* capture size */ -#define AR_SIZE_VGA 0 -#define AR_SIZE_QVGA 1 - -/* capture mode */ -#define AR_MODE_INTERLACE 0 -#define AR_MODE_NORMAL 1 - -struct ar { - struct v4l2_device v4l2_dev; - struct video_device vdev; - int start_capture; /* duaring capture in INT. mode. */ -#if USE_INT - unsigned char *line_buff; /* DMA line buffer */ -#endif - unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */ - short size; /* capture size */ - short mode; /* capture mode */ - int width, height; - int frame_bytes, line_bytes; - wait_queue_head_t wait; - struct mutex lock; -}; - -static struct ar ardev; - -static int video_nr = -1; /* video device number (first free) */ -static unsigned char yuv[MAX_AR_FRAME_BYTES]; - -/* module parameters */ -/* default frequency */ -#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */ -static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */ -static int vga; /* default mode(0:QVGA mode, other:VGA mode) */ -static int vga_interlace; /* 0 is normal mode for, else interlace mode */ -module_param(freq, int, 0); -module_param(vga, int, 0); -module_param(vga_interlace, int, 0); - -static void wait_for_vsync(void) -{ - while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ - cpu_relax(); - while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */ - cpu_relax(); -} - -static void wait_acknowledge(void) -{ - int i; - - for (i = 0; i < 1000; i++) - cpu_relax(); - while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK) - cpu_relax(); -} - -/******************************************************************* - * I2C functions - *******************************************************************/ -static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, - unsigned long data3) -{ - int i; - - /* Slave Address */ - ar_outl(addr, PLDI2CDATA); - wait_for_vsync(); - - /* Start */ - ar_outl(1, PLDI2CCND); - wait_acknowledge(); - - /* Transfer data 1 */ - ar_outl(data1, PLDI2CDATA); - wait_for_vsync(); - ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); - wait_acknowledge(); - - /* Transfer data 2 */ - ar_outl(data2, PLDI2CDATA); - wait_for_vsync(); - ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); - wait_acknowledge(); - - if (n == 3) { - /* Transfer data 3 */ - ar_outl(data3, PLDI2CDATA); - wait_for_vsync(); - ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); - wait_acknowledge(); - } - - /* Stop */ - for (i = 0; i < 100; i++) - cpu_relax(); - ar_outl(2, PLDI2CCND); - ar_outl(2, PLDI2CCND); - - while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB) - cpu_relax(); -} - - -static void init_iic(void) -{ - DEBUG(1, "init_iic:\n"); - - /* - * ICU Setting (iic) - */ - /* I2C Setting */ - ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */ - ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */ - ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */ - - /* I2C CLK */ - /* 50MH-100k */ - if (freq == 75) - ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ - else if (freq == 50) - ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ - else - ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ - ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */ -} - -/************************************************************************** - * - * Video4Linux Interface functions - * - **************************************************************************/ - -static inline void disable_dma(void) -{ - ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */ -} - -static inline void enable_dma(void) -{ - ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */ -} - -static inline void clear_dma_status(void) -{ - ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */ -} - -static void wait_for_vertical_sync(struct ar *ar, int exp_line) -{ -#if CHECK_LOST - int tmout = 10000; /* FIXME */ - int l; - - /* - * check HCOUNT because we cannot check vertical sync. - */ - for (; tmout >= 0; tmout--) { - l = ar_inl(ARVHCOUNT); - if (l == exp_line) - break; - } - if (tmout < 0) - v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l); -#else - while (ar_inl(ARVHCOUNT) != exp_line) - cpu_relax(); -#endif -} - -static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - struct ar *ar = video_drvdata(file); - long ret = ar->frame_bytes; /* return read bytes */ - unsigned long arvcr1 = 0; - unsigned long flags; - unsigned char *p; - int h, w; - unsigned char *py, *pu, *pv; -#if !USE_INT - int l; -#endif - - DEBUG(1, "ar_read()\n"); - - if (ar->size == AR_SIZE_QVGA) - arvcr1 |= ARVCR1_QVGA; - if (ar->mode == AR_MODE_NORMAL) - arvcr1 |= ARVCR1_NORMAL; - - mutex_lock(&ar->lock); - -#if USE_INT - local_irq_save(flags); - disable_dma(); - ar_outl(0xa1871300, M32R_DMA0CR0_PORTL); - ar_outl(0x01000000, M32R_DMA0CR1_PORTL); - - /* set AR FIFO address as source(BSEL5) */ - ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); - ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); - ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */ - ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */ - ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */ - ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ - - /* - * Okay, kick AR LSI to invoke an interrupt - */ - ar->start_capture = -1; - ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); - local_irq_restore(flags); - /* .... AR interrupts .... */ - wait_event_interruptible(ar->wait, ar->start_capture == 0); - if (signal_pending(current)) { - printk(KERN_ERR "arv: interrupted while get frame data.\n"); - ret = -EINTR; - goto out_up; - } -#else /* ! USE_INT */ - /* polling */ - ar_outl(arvcr1, ARVCR1); - disable_dma(); - ar_outl(0x8000, M32R_DMAEDET_PORTL); - ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); - ar_outl(0x01000000, M32R_DMA0CR1_PORTL); - ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); - ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); - ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); - ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); - - local_irq_save(flags); - while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */ - cpu_relax(); - if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { - for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(ar, h); - if (h < (AR_HEIGHT_VGA/2)) - l = h << 1; - else - l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1); - ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL); - enable_dma(); - while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) - cpu_relax(); - disable_dma(); - clear_dma_status(); - ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); - } - } else { - for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(ar, h); - ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); - enable_dma(); - while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) - cpu_relax(); - disable_dma(); - clear_dma_status(); - ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); - } - } - local_irq_restore(flags); -#endif /* ! USE_INT */ - - /* - * convert YUV422 to YUV422P - * +--------------------+ - * | Y0,Y1,... | - * | ..............Yn | - * +--------------------+ - * | U0,U1,........Un | - * +--------------------+ - * | V0,V1,........Vn | - * +--------------------+ - */ - py = yuv; - pu = py + (ar->frame_bytes / 2); - pv = pu + (ar->frame_bytes / 4); - for (h = 0; h < ar->height; h++) { - p = ar->frame[h]; - for (w = 0; w < ar->line_bytes; w += 4) { - *py++ = *p++; - *pu++ = *p++; - *py++ = *p++; - *pv++ = *p++; - } - } - if (copy_to_user(buf, yuv, ar->frame_bytes)) { - v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n"); - ret = -EFAULT; - goto out_up; - } - DEBUG(1, "ret = %d\n", ret); -out_up: - mutex_unlock(&ar->lock); - return ret; -} - -static int ar_querycap(struct file *file, void *priv, - struct v4l2_capability *vcap) -{ - struct ar *ar = video_drvdata(file); - - strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); - strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; - vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin) -{ - if (vin->index > 0) - return -EINVAL; - strlcpy(vin->name, "Camera", sizeof(vin->name)); - vin->type = V4L2_INPUT_TYPE_CAMERA; - vin->audioset = 0; - vin->tuner = 0; - vin->std = V4L2_STD_ALL; - vin->status = 0; - return 0; -} - -static int ar_g_input(struct file *file, void *fh, unsigned int *inp) -{ - *inp = 0; - return 0; -} - -static int ar_s_input(struct file *file, void *fh, unsigned int inp) -{ - return inp ? -EINVAL : 0; -} - -static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct ar *ar = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - pix->width = ar->width; - pix->height = ar->height; - pix->pixelformat = V4L2_PIX_FMT_YUV422P; - pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED; - pix->bytesperline = ar->width; - pix->sizeimage = 2 * ar->width * ar->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct ar *ar = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) { - pix->height = AR_HEIGHT_QVGA; - pix->width = AR_WIDTH_QVGA; - pix->field = V4L2_FIELD_INTERLACED; - } else { - pix->height = AR_HEIGHT_VGA; - pix->width = AR_WIDTH_VGA; - pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; - } - pix->pixelformat = V4L2_PIX_FMT_YUV422P; - pix->bytesperline = ar->width; - pix->sizeimage = 2 * ar->width * ar->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct ar *ar = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int ret = ar_try_fmt_vid_cap(file, fh, fmt); - - if (ret) - return ret; - mutex_lock(&ar->lock); - ar->width = pix->width; - ar->height = pix->height; - if (ar->width == AR_WIDTH_VGA) { - ar->size = AR_SIZE_VGA; - ar->frame_bytes = AR_FRAME_BYTES_VGA; - ar->line_bytes = AR_LINE_BYTES_VGA; - if (vga_interlace) - ar->mode = AR_MODE_INTERLACE; - else - ar->mode = AR_MODE_NORMAL; - } else { - ar->size = AR_SIZE_QVGA; - ar->frame_bytes = AR_FRAME_BYTES_QVGA; - ar->line_bytes = AR_LINE_BYTES_QVGA; - ar->mode = AR_MODE_INTERLACE; - } - /* Ok we figured out what to use from our wide choice */ - mutex_unlock(&ar->lock); - return 0; -} - -static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) -{ - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P, - { 0, 0, 0, 0 } - }, - }; - enum v4l2_buf_type type = fmt->type; - - if (fmt->index > 0) - return -EINVAL; - - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; -} - -#if USE_INT -/* - * Interrupt handler - */ -static void ar_interrupt(int irq, void *dev) -{ - struct ar *ar = dev; - unsigned int line_count; - unsigned int line_number; - unsigned int arvcr1; - - line_count = ar_inl(ARVHCOUNT); /* line number */ - if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { - /* operations for interlace mode */ - if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */ - line_number = (line_count << 1); - else /* odd line */ - line_number = - (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1); - } else { - line_number = line_count; - } - - if (line_number == 0) { - /* - * It is an interrupt for line 0. - * we have to start capture. - */ - disable_dma(); -#if 0 - ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ -#endif - memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); -#if 0 - ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); -#endif - enable_dma(); - ar->start_capture = 1; /* during capture */ - return; - } - - if (ar->start_capture == 1 && line_number <= (ar->height - 1)) { - disable_dma(); - memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes); - - /* - * if captured all line of a frame, disable AR interrupt - * and wake a process up. - */ - if (line_number == (ar->height - 1)) { /* end of line */ - - ar->start_capture = 0; - - /* disable AR interrupt request */ - arvcr1 = ar_inl(ARVCR1); - arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */ - ar_outl(arvcr1, ARVCR1); /* disable */ - wake_up_interruptible(&ar->wait); - } else { -#if 0 - ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); - ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); -#endif - enable_dma(); - } - } -} -#endif - -/* - * ar_initialize() - * ar_initialize() is called by video_register_device() and - * initializes AR LSI and peripherals. - * - * -1 is returned in all failures. - * 0 is returned in success. - * - */ -static int ar_initialize(struct ar *ar) -{ - unsigned long cr = 0; - int i, found = 0; - - DEBUG(1, "ar_initialize:\n"); - - /* - * initialize AR LSI - */ - ar_outl(0, ARVCR0); /* assert reset of AR LSI */ - for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */ - cpu_relax(); - ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */ - for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */ - cpu_relax(); - - /* AR uses INT3 of CPU as interrupt pin. */ - ar_outl(ARINTSEL_INT3, ARINTSEL); - - if (ar->size == AR_SIZE_QVGA) - cr |= ARVCR1_QVGA; - if (ar->mode == AR_MODE_NORMAL) - cr |= ARVCR1_NORMAL; - ar_outl(cr, ARVCR1); - - /* - * Initialize IIC so that CPU can communicate with AR LSI, - * and send boot commands to AR LSI. - */ - init_iic(); - - for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */ - if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */ - found = 1; - break; - } - } - - if (found == 0) - return -ENODEV; - - v4l2_info(&ar->v4l2_dev, "Initializing "); - - iic(2, 0x78, 0x11, 0x01, 0x00); /* start */ - iic(3, 0x78, 0x12, 0x00, 0x06); - iic(3, 0x78, 0x12, 0x12, 0x30); - iic(3, 0x78, 0x12, 0x15, 0x58); - iic(3, 0x78, 0x12, 0x17, 0x30); - printk(KERN_CONT "."); - iic(3, 0x78, 0x12, 0x1a, 0x97); - iic(3, 0x78, 0x12, 0x1b, 0xff); - iic(3, 0x78, 0x12, 0x1c, 0xff); - iic(3, 0x78, 0x12, 0x26, 0x10); - iic(3, 0x78, 0x12, 0x27, 0x00); - printk(KERN_CONT "."); - iic(2, 0x78, 0x34, 0x02, 0x00); - iic(2, 0x78, 0x7a, 0x10, 0x00); - iic(2, 0x78, 0x80, 0x39, 0x00); - iic(2, 0x78, 0x81, 0xe6, 0x00); - iic(2, 0x78, 0x8d, 0x00, 0x00); - printk(KERN_CONT "."); - iic(2, 0x78, 0x8e, 0x0c, 0x00); - iic(2, 0x78, 0x8f, 0x00, 0x00); -#if 0 - iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */ -#endif - iic(2, 0x78, 0x93, 0x01, 0x00); - iic(2, 0x78, 0x94, 0xcd, 0x00); - iic(2, 0x78, 0x95, 0x00, 0x00); - printk(KERN_CONT "."); - iic(2, 0x78, 0x96, 0xa0, 0x00); - iic(2, 0x78, 0x97, 0x00, 0x00); - iic(2, 0x78, 0x98, 0x60, 0x00); - iic(2, 0x78, 0x99, 0x01, 0x00); - iic(2, 0x78, 0x9a, 0x19, 0x00); - printk(KERN_CONT "."); - iic(2, 0x78, 0x9b, 0x02, 0x00); - iic(2, 0x78, 0x9c, 0xe8, 0x00); - iic(2, 0x78, 0x9d, 0x02, 0x00); - iic(2, 0x78, 0x9e, 0x2e, 0x00); - iic(2, 0x78, 0xb8, 0x78, 0x00); - iic(2, 0x78, 0xba, 0x05, 0x00); -#if 0 - iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */ -#endif - printk(KERN_CONT "."); - - /* color correction */ - iic(3, 0x78, 0x49, 0x00, 0x95); /* a */ - iic(3, 0x78, 0x49, 0x01, 0x96); /* b */ - iic(3, 0x78, 0x49, 0x03, 0x85); /* c */ - iic(3, 0x78, 0x49, 0x04, 0x97); /* d */ - iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */ - iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */ - iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */ - iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */ - iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */ - - printk(KERN_CONT "."); - iic(2, 0x78, 0x11, 0x00, 0x00); /* end */ - printk(KERN_CONT " done\n"); - return 0; -} - - -/**************************************************************************** - * - * Video4Linux Module functions - * - ****************************************************************************/ - -static const struct v4l2_file_operations ar_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .read = ar_read, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops ar_ioctl_ops = { - .vidioc_querycap = ar_querycap, - .vidioc_g_input = ar_g_input, - .vidioc_s_input = ar_s_input, - .vidioc_enum_input = ar_enum_input, - .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap, -}; - -#define ALIGN4(x) ((((int)(x)) & 0x3) == 0) - -static int __init ar_init(void) -{ - struct ar *ar; - struct v4l2_device *v4l2_dev; - int ret; - int i; - - ar = &ardev; - v4l2_dev = &ar->v4l2_dev; - strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name)); - v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION); - - ret = v4l2_device_register(NULL, v4l2_dev); - if (ret < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return ret; - } - ret = -EIO; - -#if USE_INT - /* allocate a DMA buffer for 1 line. */ - ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA); - if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) { - v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n"); - ret = -ENOMEM; - goto out_end; - } -#endif - /* allocate buffers for a frame */ - for (i = 0; i < MAX_AR_HEIGHT; i++) { - ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL); - if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) { - v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n"); - ret = -ENOMEM; - goto out_line_buff; - } - } - - strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name)); - ar->vdev.v4l2_dev = v4l2_dev; - ar->vdev.fops = &ar_fops; - ar->vdev.ioctl_ops = &ar_ioctl_ops; - ar->vdev.release = video_device_release_empty; - video_set_drvdata(&ar->vdev, ar); - - if (vga) { - ar->width = AR_WIDTH_VGA; - ar->height = AR_HEIGHT_VGA; - ar->size = AR_SIZE_VGA; - ar->frame_bytes = AR_FRAME_BYTES_VGA; - ar->line_bytes = AR_LINE_BYTES_VGA; - if (vga_interlace) - ar->mode = AR_MODE_INTERLACE; - else - ar->mode = AR_MODE_NORMAL; - } else { - ar->width = AR_WIDTH_QVGA; - ar->height = AR_HEIGHT_QVGA; - ar->size = AR_SIZE_QVGA; - ar->frame_bytes = AR_FRAME_BYTES_QVGA; - ar->line_bytes = AR_LINE_BYTES_QVGA; - ar->mode = AR_MODE_INTERLACE; - } - mutex_init(&ar->lock); - init_waitqueue_head(&ar->wait); - -#if USE_INT - if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) { - v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3); - ret = -EIO; - goto out_irq; - } -#endif - - if (ar_initialize(ar) != 0) { - v4l2_err(v4l2_dev, "M64278 not found.\n"); - ret = -ENODEV; - goto out_dev; - } - - /* - * ok, we can initialize h/w according to parameters, - * so register video device as a frame grabber type. - * device is named "video[0-64]". - * video_register_device() initializes h/w using ar_initialize(). - */ - if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { - /* return -1, -ENFILE(full) or others */ - v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n"); - ret = -ENODEV; - goto out_dev; - } - - v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", - video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq); - - return 0; - -out_dev: -#if USE_INT - free_irq(M32R_IRQ_INT3, ar); - -out_irq: -#endif - for (i = 0; i < MAX_AR_HEIGHT; i++) - kfree(ar->frame[i]); - -out_line_buff: -#if USE_INT - kfree(ar->line_buff); - -out_end: -#endif - v4l2_device_unregister(&ar->v4l2_dev); - return ret; -} - - -static int __init ar_init_module(void) -{ - freq = (boot_cpu_data.bus_clock / 1000000); - printk(KERN_INFO "arv: Bus clock %d\n", freq); - if (freq != 50 && freq != 75) - freq = DEFAULT_FREQ; - return ar_init(); -} - -static void __exit ar_cleanup_module(void) -{ - struct ar *ar; - int i; - - ar = &ardev; - video_unregister_device(&ar->vdev); -#if USE_INT - free_irq(M32R_IRQ_INT3, ar); -#endif - for (i = 0; i < MAX_AR_HEIGHT; i++) - kfree(ar->frame[i]); -#if USE_INT - kfree(ar->line_buff); -#endif - v4l2_device_unregister(&ar->v4l2_dev); -} - -module_init(ar_init_module); -module_exit(ar_cleanup_module); - -MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>"); -MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(VERSION); diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index 34676409ca08..d89e14524d42 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -335,7 +335,7 @@ static struct isc_format formats_list[] = { }, }; -struct fmt_config fmt_configs_list[] = { +static struct fmt_config fmt_configs_list[] = { { .fourcc = V4L2_PIX_FMT_SBGGR8, .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, @@ -1417,20 +1417,14 @@ static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct isc_device *isc = video_drvdata(file); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_subdev_call(isc->current_subdev->sd, video, g_parm, a); + return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); } static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct isc_device *isc = video_drvdata(file); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_subdev_call(isc->current_subdev->sd, video, s_parm, a); + return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); } static int isc_enum_framesizes(struct file *file, void *fh, diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 9958918e2449..e5be21a31640 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -689,22 +689,14 @@ static int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct atmel_isi *isi = video_drvdata(file); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - a->parm.capture.readbuffers = 2; - return v4l2_subdev_call(isi->entity.subdev, video, g_parm, a); + return v4l2_g_parm_cap(video_devdata(file), isi->entity.subdev, a); } static int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct atmel_isi *isi = video_drvdata(file); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - a->parm.capture.readbuffers = 2; - return v4l2_subdev_call(isi->entity.subdev, video, s_parm, a); + return v4l2_s_parm_cap(video_devdata(file), isi->entity.subdev, a); } static int isi_enum_framesizes(struct file *file, void *fh, diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig deleted file mode 100644 index 68fa90151b8f..000000000000 --- a/drivers/media/platform/blackfin/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config VIDEO_BLACKFIN_CAPTURE - tristate "Blackfin Video Capture Driver" - depends on VIDEO_V4L2 && BLACKFIN && I2C - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - help - V4L2 bridge driver for Blackfin video capture device. - Choose PPI or EPPI as its interface. - - To compile this driver as a module, choose M here: the - module will be called bfin_capture. - -config VIDEO_BLACKFIN_PPI - tristate - depends on VIDEO_BLACKFIN_CAPTURE - default VIDEO_BLACKFIN_CAPTURE diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile deleted file mode 100644 index 30421bc23080..000000000000 --- a/drivers/media/platform/blackfin/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o -obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c deleted file mode 100644 index 41f179117fb0..000000000000 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Analog Devices video capture driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * 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 <linux/completion.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/types.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/videobuf2-dma-contig.h> - -#include <asm/dma.h> - -#include <media/blackfin/bfin_capture.h> -#include <media/blackfin/ppi.h> - -#define CAPTURE_DRV_NAME "bfin_capture" - -struct bcap_format { - char *desc; - u32 pixelformat; - u32 mbus_code; - int bpp; /* bits per pixel */ - int dlen; /* data length for ppi in bits */ -}; - -struct bcap_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct bcap_device { - /* capture device instance */ - struct v4l2_device v4l2_dev; - /* v4l2 control handler */ - struct v4l2_ctrl_handler ctrl_handler; - /* device node data */ - struct video_device video_dev; - /* sub device instance */ - struct v4l2_subdev *sd; - /* capture config */ - struct bfin_capture_config *cfg; - /* ppi interface */ - struct ppi_if *ppi; - /* current input */ - unsigned int cur_input; - /* current selected standard */ - v4l2_std_id std; - /* current selected dv_timings */ - struct v4l2_dv_timings dv_timings; - /* used to store pixel format */ - struct v4l2_pix_format fmt; - /* bits per pixel*/ - int bpp; - /* data length for ppi in bits */ - int dlen; - /* used to store sensor supported format */ - struct bcap_format *sensor_formats; - /* number of sensor formats array */ - int num_sensor_formats; - /* pointing to current video buffer */ - struct bcap_buffer *cur_frm; - /* buffer queue used in videobuf2 */ - struct vb2_queue buffer_queue; - /* queue of filled frames */ - struct list_head dma_queue; - /* used in videobuf2 callback */ - spinlock_t lock; - /* used to access capture device */ - struct mutex mutex; - /* used to wait ppi to complete one transfer */ - struct completion comp; - /* prepare to stop */ - bool stop; - /* vb2 buffer sequence counter */ - unsigned sequence; -}; - -static const struct bcap_format bcap_formats[] = { - { - .desc = "YCbCr 4:2:2 Interleaved UYVY", - .pixelformat = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - .dlen = 8, - }, - { - .desc = "YCbCr 4:2:2 Interleaved YUYV", - .pixelformat = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - .dlen = 8, - }, - { - .desc = "YCbCr 4:2:2 Interleaved UYVY", - .pixelformat = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, - .bpp = 16, - .dlen = 16, - }, - { - .desc = "RGB 565", - .pixelformat = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - .dlen = 8, - }, - { - .desc = "RGB 444", - .pixelformat = V4L2_PIX_FMT_RGB444, - .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, - .bpp = 16, - .dlen = 8, - }, - -}; -#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats) - -static irqreturn_t bcap_isr(int irq, void *dev_id); - -static struct bcap_buffer *to_bcap_vb(struct vb2_v4l2_buffer *vb) -{ - return container_of(vb, struct bcap_buffer, vb); -} - -static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) -{ - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct bcap_format *sf; - unsigned int num_formats = 0; - int i, j; - - while (!v4l2_subdev_call(bcap_dev->sd, pad, - enum_mbus_code, NULL, &code)) { - num_formats++; - code.index++; - } - if (!num_formats) - return -ENXIO; - - sf = kcalloc(num_formats, sizeof(*sf), GFP_KERNEL); - if (!sf) - return -ENOMEM; - - for (i = 0; i < num_formats; i++) { - code.index = i; - v4l2_subdev_call(bcap_dev->sd, pad, - enum_mbus_code, NULL, &code); - for (j = 0; j < BCAP_MAX_FMTS; j++) - if (code.code == bcap_formats[j].mbus_code) - break; - if (j == BCAP_MAX_FMTS) { - /* we don't allow this sensor working with our bridge */ - kfree(sf); - return -EINVAL; - } - sf[i] = bcap_formats[j]; - } - bcap_dev->sensor_formats = sf; - bcap_dev->num_sensor_formats = num_formats; - return 0; -} - -static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) -{ - bcap_dev->num_sensor_formats = 0; - kfree(bcap_dev->sensor_formats); - bcap_dev->sensor_formats = NULL; -} - -static int bcap_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - 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; - - if (*nplanes) - return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = bcap_dev->fmt.sizeimage; - - return 0; -} - -static int bcap_buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = bcap_dev->fmt.sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - vb2_set_plane_payload(vb, 0, size); - - vbuf->field = bcap_dev->fmt.field; - - return 0; -} - -static void bcap_buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - struct bcap_buffer *buf = to_bcap_vb(vbuf); - unsigned long flags; - - spin_lock_irqsave(&bcap_dev->lock, flags); - list_add_tail(&buf->list, &bcap_dev->dma_queue); - spin_unlock_irqrestore(&bcap_dev->lock, flags); -} - -static void bcap_buffer_cleanup(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - struct bcap_buffer *buf = to_bcap_vb(vbuf); - unsigned long flags; - - spin_lock_irqsave(&bcap_dev->lock, flags); - list_del_init(&buf->list); - spin_unlock_irqrestore(&bcap_dev->lock, flags); -} - -static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); - struct ppi_if *ppi = bcap_dev->ppi; - struct bcap_buffer *buf, *tmp; - struct ppi_params params; - dma_addr_t addr; - int ret; - - /* enable streamon on the sub device */ - ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1); - if (ret && (ret != -ENOIOCTLCMD)) { - v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n"); - goto err; - } - - /* set ppi params */ - params.width = bcap_dev->fmt.width; - params.height = bcap_dev->fmt.height; - params.bpp = bcap_dev->bpp; - params.dlen = bcap_dev->dlen; - params.ppi_control = bcap_dev->cfg->ppi_control; - params.int_mask = bcap_dev->cfg->int_mask; - if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities - & V4L2_IN_CAP_DV_TIMINGS) { - struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; - - params.hdelay = bt->hsync + bt->hbackporch; - params.vdelay = bt->vsync + bt->vbackporch; - params.line = V4L2_DV_BT_FRAME_WIDTH(bt); - params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt); - } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities - & V4L2_IN_CAP_STD) { - params.hdelay = 0; - params.vdelay = 0; - if (bcap_dev->std & V4L2_STD_525_60) { - params.line = 858; - params.frame = 525; - } else { - params.line = 864; - params.frame = 625; - } - } else { - params.hdelay = 0; - params.vdelay = 0; - params.line = params.width + bcap_dev->cfg->blank_pixels; - params.frame = params.height; - } - ret = ppi->ops->set_params(ppi, ¶ms); - if (ret < 0) { - v4l2_err(&bcap_dev->v4l2_dev, - "Error in setting ppi params\n"); - goto err; - } - - /* attach ppi DMA irq handler */ - ret = ppi->ops->attach_irq(ppi, bcap_isr); - if (ret < 0) { - v4l2_err(&bcap_dev->v4l2_dev, - "Error in attaching interrupt handler\n"); - goto err; - } - - bcap_dev->sequence = 0; - - reinit_completion(&bcap_dev->comp); - bcap_dev->stop = false; - - /* get the next frame from the dma queue */ - bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - /* remove buffer from the dma queue */ - list_del_init(&bcap_dev->cur_frm->list); - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb.vb2_buf, - 0); - /* update DMA address */ - ppi->ops->update_addr(ppi, (unsigned long)addr); - /* enable ppi */ - ppi->ops->start(ppi); - - return 0; - -err: - list_for_each_entry_safe(buf, tmp, &bcap_dev->dma_queue, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - - return ret; -} - -static void bcap_stop_streaming(struct vb2_queue *vq) -{ - struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); - struct ppi_if *ppi = bcap_dev->ppi; - int ret; - - bcap_dev->stop = true; - wait_for_completion(&bcap_dev->comp); - ppi->ops->stop(ppi); - ppi->ops->detach_irq(ppi); - ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0); - if (ret && (ret != -ENOIOCTLCMD)) - v4l2_err(&bcap_dev->v4l2_dev, - "stream off failed in subdev\n"); - - /* release all active buffers */ - if (bcap_dev->cur_frm) - vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - - while (!list_empty(&bcap_dev->dma_queue)) { - bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - list_del_init(&bcap_dev->cur_frm->list); - vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } -} - -static const struct vb2_ops bcap_video_qops = { - .queue_setup = bcap_queue_setup, - .buf_prepare = bcap_buffer_prepare, - .buf_cleanup = bcap_buffer_cleanup, - .buf_queue = bcap_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = bcap_start_streaming, - .stop_streaming = bcap_stop_streaming, -}; - -static irqreturn_t bcap_isr(int irq, void *dev_id) -{ - struct ppi_if *ppi = dev_id; - struct bcap_device *bcap_dev = ppi->priv; - struct vb2_v4l2_buffer *vbuf = &bcap_dev->cur_frm->vb; - struct vb2_buffer *vb = &vbuf->vb2_buf; - dma_addr_t addr; - - spin_lock(&bcap_dev->lock); - - if (!list_empty(&bcap_dev->dma_queue)) { - vb->timestamp = ktime_get_ns(); - if (ppi->err) { - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - ppi->err = false; - } else { - vbuf->sequence = bcap_dev->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } - bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - list_del_init(&bcap_dev->cur_frm->list); - } else { - /* clear error flag, we will get a new frame */ - if (ppi->err) - ppi->err = false; - } - - ppi->ops->stop(ppi); - - if (bcap_dev->stop) { - complete(&bcap_dev->comp); - } else { - addr = vb2_dma_contig_plane_dma_addr( - &bcap_dev->cur_frm->vb.vb2_buf, 0); - ppi->ops->update_addr(ppi, (unsigned long)addr); - ppi->ops->start(ppi); - } - - spin_unlock(&bcap_dev->lock); - - return IRQ_HANDLED; -} - -static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_STD)) - return -ENODATA; - - return v4l2_subdev_call(bcap_dev->sd, video, querystd, std); -} - -static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_STD)) - return -ENODATA; - - *std = bcap_dev->std; - return 0; -} - -static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - int ret; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_STD)) - return -ENODATA; - - if (vb2_is_busy(&bcap_dev->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std); - if (ret < 0) - return ret; - - bcap_dev->std = std; - return 0; -} - -static int bcap_enum_dv_timings(struct file *file, void *priv, - struct v4l2_enum_dv_timings *timings) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) - return -ENODATA; - - timings->pad = 0; - - return v4l2_subdev_call(bcap_dev->sd, pad, - enum_dv_timings, timings); -} - -static int bcap_query_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) - return -ENODATA; - - return v4l2_subdev_call(bcap_dev->sd, video, - query_dv_timings, timings); -} - -static int bcap_g_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) - return -ENODATA; - - *timings = bcap_dev->dv_timings; - return 0; -} - -static int bcap_s_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_input input; - int ret; - - input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; - if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) - return -ENODATA; - - if (vb2_is_busy(&bcap_dev->buffer_queue)) - return -EBUSY; - - ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); - if (ret < 0) - return ret; - - bcap_dev->dv_timings = *timings; - return 0; -} - -static int bcap_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct bfin_capture_config *config = bcap_dev->cfg; - int ret; - u32 status; - - if (input->index >= config->num_inputs) - return -EINVAL; - - *input = config->inputs[input->index]; - /* get input status */ - ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status); - if (!ret) - input->status = status; - return 0; -} - -static int bcap_g_input(struct file *file, void *priv, unsigned int *index) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - *index = bcap_dev->cur_input; - return 0; -} - -static int bcap_s_input(struct file *file, void *priv, unsigned int index) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct bfin_capture_config *config = bcap_dev->cfg; - struct bcap_route *route; - int ret; - - if (vb2_is_busy(&bcap_dev->buffer_queue)) - return -EBUSY; - - if (index >= config->num_inputs) - return -EINVAL; - - route = &config->routes[index]; - ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, - route->input, route->output, 0); - if ((ret < 0) && (ret != -ENOIOCTLCMD)) { - v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); - return ret; - } - bcap_dev->cur_input = index; - /* if this route has specific config, update ppi control */ - if (route->ppi_control) - config->ppi_control = route->ppi_control; - return 0; -} - -static int bcap_try_format(struct bcap_device *bcap, - struct v4l2_pix_format *pixfmt, - struct bcap_format *bcap_fmt) -{ - struct bcap_format *sf = bcap->sensor_formats; - struct bcap_format *fmt = NULL; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret, i; - - for (i = 0; i < bcap->num_sensor_formats; i++) { - fmt = &sf[i]; - if (pixfmt->pixelformat == fmt->pixelformat) - break; - } - if (i == bcap->num_sensor_formats) - fmt = &sf[0]; - - v4l2_fill_mbus_format(&format.format, pixfmt, fmt->mbus_code); - ret = v4l2_subdev_call(bcap->sd, pad, set_fmt, &pad_cfg, - &format); - if (ret < 0) - return ret; - v4l2_fill_pix_format(pixfmt, &format.format); - if (bcap_fmt) { - for (i = 0; i < bcap->num_sensor_formats; i++) { - fmt = &sf[i]; - if (format.format.code == fmt->mbus_code) - break; - } - *bcap_fmt = *fmt; - } - pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; - pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; - return 0; -} - -static int bcap_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct bcap_format *sf = bcap_dev->sensor_formats; - - if (fmt->index >= bcap_dev->num_sensor_formats) - return -EINVAL; - - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strlcpy(fmt->description, - sf[fmt->index].desc, - sizeof(fmt->description)); - fmt->pixelformat = sf[fmt->index].pixelformat; - return 0; -} - -static int bcap_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - - return bcap_try_format(bcap_dev, pixfmt, NULL); -} - -static int bcap_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - fmt->fmt.pix = bcap_dev->fmt; - return 0; -} - -static int bcap_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct bcap_format bcap_fmt; - struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret; - - if (vb2_is_busy(&bcap_dev->buffer_queue)) - return -EBUSY; - - /* see if format works */ - ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); - if (ret < 0) - return ret; - - v4l2_fill_mbus_format(&format.format, pixfmt, bcap_fmt.mbus_code); - ret = v4l2_subdev_call(bcap_dev->sd, pad, set_fmt, NULL, &format); - if (ret < 0) - return ret; - bcap_dev->fmt = *pixfmt; - bcap_dev->bpp = bcap_fmt.bpp; - bcap_dev->dlen = bcap_fmt.dlen; - return 0; -} - -static int bcap_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); - strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info)); - strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card)); - return 0; -} - -static int bcap_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a); -} - -static int bcap_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a); -} - -static int bcap_log_status(struct file *file, void *priv) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - /* status for sub devices */ - v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status); - return 0; -} - -static const struct v4l2_ioctl_ops bcap_ioctl_ops = { - .vidioc_querycap = bcap_querycap, - .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap, - .vidioc_enum_input = bcap_enum_input, - .vidioc_g_input = bcap_g_input, - .vidioc_s_input = bcap_s_input, - .vidioc_querystd = bcap_querystd, - .vidioc_s_std = bcap_s_std, - .vidioc_g_std = bcap_g_std, - .vidioc_s_dv_timings = bcap_s_dv_timings, - .vidioc_g_dv_timings = bcap_g_dv_timings, - .vidioc_query_dv_timings = bcap_query_dv_timings, - .vidioc_enum_dv_timings = bcap_enum_dv_timings, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_parm = bcap_g_parm, - .vidioc_s_parm = bcap_s_parm, - .vidioc_log_status = bcap_log_status, -}; - -static const struct v4l2_file_operations bcap_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -#ifndef CONFIG_MMU - .get_unmapped_area = vb2_fop_get_unmapped_area, -#endif - .poll = vb2_fop_poll -}; - -static int bcap_probe(struct platform_device *pdev) -{ - struct bcap_device *bcap_dev; - struct video_device *vfd; - struct i2c_adapter *i2c_adap; - struct bfin_capture_config *config; - struct vb2_queue *q; - struct bcap_route *route; - int ret; - - config = pdev->dev.platform_data; - if (!config || !config->num_inputs) { - v4l2_err(pdev->dev.driver, "Unable to get board config\n"); - return -ENODEV; - } - - bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL); - if (!bcap_dev) - return -ENOMEM; - - bcap_dev->cfg = config; - - bcap_dev->ppi = ppi_create_instance(pdev, config->ppi_info); - if (!bcap_dev->ppi) { - v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); - ret = -ENODEV; - goto err_free_dev; - } - bcap_dev->ppi->priv = bcap_dev; - - vfd = &bcap_dev->video_dev; - /* initialize field of video device */ - vfd->release = video_device_release_empty; - vfd->fops = &bcap_fops; - vfd->ioctl_ops = &bcap_ioctl_ops; - vfd->tvnorms = 0; - vfd->v4l2_dev = &bcap_dev->v4l2_dev; - strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); - - ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev); - if (ret) { - v4l2_err(pdev->dev.driver, - "Unable to register v4l2 device\n"); - goto err_free_ppi; - } - v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); - - bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler; - ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0); - if (ret) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to init control handler\n"); - goto err_unreg_v4l2; - } - - spin_lock_init(&bcap_dev->lock); - /* initialize queue */ - q = &bcap_dev->buffer_queue; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF; - q->drv_priv = bcap_dev; - q->buf_struct_size = sizeof(struct bcap_buffer); - q->ops = &bcap_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - 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) - goto err_free_handler; - - mutex_init(&bcap_dev->mutex); - init_completion(&bcap_dev->comp); - - /* init video dma queues */ - INIT_LIST_HEAD(&bcap_dev->dma_queue); - - vfd->lock = &bcap_dev->mutex; - vfd->queue = q; - - /* register video device */ - ret = video_register_device(&bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); - if (ret) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to register video device\n"); - goto err_free_handler; - } - video_set_drvdata(&bcap_dev->video_dev, bcap_dev); - v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n", - video_device_node_name(vfd)); - - /* load up the subdevice */ - i2c_adap = i2c_get_adapter(config->i2c_adapter_id); - if (!i2c_adap) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to find i2c adapter\n"); - ret = -ENODEV; - goto err_unreg_vdev; - - } - bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev, - i2c_adap, - &config->board_info, - NULL); - if (bcap_dev->sd) { - int i; - - /* update tvnorms from the sub devices */ - for (i = 0; i < config->num_inputs; i++) - vfd->tvnorms |= config->inputs[i].std; - } else { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to register sub device\n"); - ret = -ENODEV; - goto err_unreg_vdev; - } - - v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); - - /* - * explicitly set input, otherwise some boards - * may not work at the state as we expected - */ - route = &config->routes[0]; - ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, - route->input, route->output, 0); - if ((ret < 0) && (ret != -ENOIOCTLCMD)) { - v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); - goto err_unreg_vdev; - } - bcap_dev->cur_input = 0; - /* if this route has specific config, update ppi control */ - if (route->ppi_control) - config->ppi_control = route->ppi_control; - - /* now we can probe the default state */ - if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { - v4l2_std_id std; - ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std); - if (ret) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to get std\n"); - goto err_unreg_vdev; - } - bcap_dev->std = std; - } - if (config->inputs[0].capabilities & V4L2_IN_CAP_DV_TIMINGS) { - struct v4l2_dv_timings dv_timings; - ret = v4l2_subdev_call(bcap_dev->sd, video, - g_dv_timings, &dv_timings); - if (ret) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to get dv timings\n"); - goto err_unreg_vdev; - } - bcap_dev->dv_timings = dv_timings; - } - ret = bcap_init_sensor_formats(bcap_dev); - if (ret) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to create sensor formats table\n"); - goto err_unreg_vdev; - } - return 0; -err_unreg_vdev: - video_unregister_device(&bcap_dev->video_dev); -err_free_handler: - v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); -err_unreg_v4l2: - v4l2_device_unregister(&bcap_dev->v4l2_dev); -err_free_ppi: - ppi_delete_instance(bcap_dev->ppi); -err_free_dev: - kfree(bcap_dev); - return ret; -} - -static int bcap_remove(struct platform_device *pdev) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); - struct bcap_device *bcap_dev = container_of(v4l2_dev, - struct bcap_device, v4l2_dev); - - bcap_free_sensor_formats(bcap_dev); - video_unregister_device(&bcap_dev->video_dev); - v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); - v4l2_device_unregister(v4l2_dev); - ppi_delete_instance(bcap_dev->ppi); - kfree(bcap_dev); - return 0; -} - -static struct platform_driver bcap_driver = { - .driver = { - .name = CAPTURE_DRV_NAME, - }, - .probe = bcap_probe, - .remove = bcap_remove, -}; -module_platform_driver(bcap_driver); - -MODULE_DESCRIPTION("Analog Devices blackfin video capture driver"); -MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c deleted file mode 100644 index d3dc765c1609..000000000000 --- a/drivers/media/platform/blackfin/ppi.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * ppi.c Analog Devices Parallel Peripheral Interface driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * 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 <linux/module.h> -#include <linux/slab.h> -#include <linux/platform_device.h> - -#include <asm/bfin_ppi.h> -#include <asm/blackfin.h> -#include <asm/cacheflush.h> -#include <asm/dma.h> -#include <asm/portmux.h> - -#include <media/blackfin/ppi.h> - -static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler); -static void ppi_detach_irq(struct ppi_if *ppi); -static int ppi_start(struct ppi_if *ppi); -static int ppi_stop(struct ppi_if *ppi); -static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params); -static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr); - -static const struct ppi_ops ppi_ops = { - .attach_irq = ppi_attach_irq, - .detach_irq = ppi_detach_irq, - .start = ppi_start, - .stop = ppi_stop, - .set_params = ppi_set_params, - .update_addr = ppi_update_addr, -}; - -static irqreturn_t ppi_irq_err(int irq, void *dev_id) -{ - struct ppi_if *ppi = dev_id; - const struct ppi_info *info = ppi->info; - - switch (info->type) { - case PPI_TYPE_PPI: - { - struct bfin_ppi_regs *reg = info->base; - unsigned short status; - - /* register on bf561 is cleared when read - * others are W1C - */ - status = bfin_read16(®->status); - if (status & 0x3000) - ppi->err = true; - bfin_write16(®->status, 0xff00); - break; - } - case PPI_TYPE_EPPI: - { - struct bfin_eppi_regs *reg = info->base; - unsigned short status; - - status = bfin_read16(®->status); - if (status & 0x2) - ppi->err = true; - bfin_write16(®->status, 0xffff); - break; - } - case PPI_TYPE_EPPI3: - { - struct bfin_eppi3_regs *reg = info->base; - unsigned long stat; - - stat = bfin_read32(®->stat); - if (stat & 0x2) - ppi->err = true; - bfin_write32(®->stat, 0xc0ff); - break; - } - default: - break; - } - - return IRQ_HANDLED; -} - -static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler) -{ - const struct ppi_info *info = ppi->info; - int ret; - - ret = request_dma(info->dma_ch, "PPI_DMA"); - - if (ret) { - pr_err("Unable to allocate DMA channel for PPI\n"); - return ret; - } - set_dma_callback(info->dma_ch, handler, ppi); - - if (ppi->err_int) { - ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi); - if (ret) { - pr_err("Unable to allocate IRQ for PPI\n"); - free_dma(info->dma_ch); - } - } - return ret; -} - -static void ppi_detach_irq(struct ppi_if *ppi) -{ - const struct ppi_info *info = ppi->info; - - if (ppi->err_int) - free_irq(info->irq_err, ppi); - free_dma(info->dma_ch); -} - -static int ppi_start(struct ppi_if *ppi) -{ - const struct ppi_info *info = ppi->info; - - /* enable DMA */ - enable_dma(info->dma_ch); - - /* enable PPI */ - ppi->ppi_control |= PORT_EN; - switch (info->type) { - case PPI_TYPE_PPI: - { - struct bfin_ppi_regs *reg = info->base; - bfin_write16(®->control, ppi->ppi_control); - break; - } - case PPI_TYPE_EPPI: - { - struct bfin_eppi_regs *reg = info->base; - bfin_write32(®->control, ppi->ppi_control); - break; - } - case PPI_TYPE_EPPI3: - { - struct bfin_eppi3_regs *reg = info->base; - bfin_write32(®->ctl, ppi->ppi_control); - break; - } - default: - return -EINVAL; - } - - SSYNC(); - return 0; -} - -static int ppi_stop(struct ppi_if *ppi) -{ - const struct ppi_info *info = ppi->info; - - /* disable PPI */ - ppi->ppi_control &= ~PORT_EN; - switch (info->type) { - case PPI_TYPE_PPI: - { - struct bfin_ppi_regs *reg = info->base; - bfin_write16(®->control, ppi->ppi_control); - break; - } - case PPI_TYPE_EPPI: - { - struct bfin_eppi_regs *reg = info->base; - bfin_write32(®->control, ppi->ppi_control); - break; - } - case PPI_TYPE_EPPI3: - { - struct bfin_eppi3_regs *reg = info->base; - bfin_write32(®->ctl, ppi->ppi_control); - break; - } - default: - return -EINVAL; - } - - /* disable DMA */ - clear_dma_irqstat(info->dma_ch); - disable_dma(info->dma_ch); - - SSYNC(); - return 0; -} - -static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) -{ - const struct ppi_info *info = ppi->info; - int dma32 = 0; - int dma_config, bytes_per_line; - int hcount, hdelay, samples_per_line; - -#ifdef CONFIG_PINCTRL - static const char * const pin_state[] = {"8bit", "16bit", "24bit"}; - struct pinctrl *pctrl; - struct pinctrl_state *pstate; - - if (params->dlen > 24 || params->dlen <= 0) - return -EINVAL; - pctrl = devm_pinctrl_get(ppi->dev); - if (IS_ERR(pctrl)) - return PTR_ERR(pctrl); - pstate = pinctrl_lookup_state(pctrl, - pin_state[(params->dlen + 7) / 8 - 1]); - if (pinctrl_select_state(pctrl, pstate)) - return -EINVAL; -#endif - - bytes_per_line = params->width * params->bpp / 8; - /* convert parameters unit from pixels to samples */ - hcount = params->width * params->bpp / params->dlen; - hdelay = params->hdelay * params->bpp / params->dlen; - samples_per_line = params->line * params->bpp / params->dlen; - if (params->int_mask == 0xFFFFFFFF) - ppi->err_int = false; - else - ppi->err_int = true; - - dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); - ppi->ppi_control = params->ppi_control & ~PORT_EN; - if (!(ppi->ppi_control & PORT_DIR)) - dma_config |= WNR; - switch (info->type) { - case PPI_TYPE_PPI: - { - struct bfin_ppi_regs *reg = info->base; - - if (params->ppi_control & DMA32) - dma32 = 1; - - bfin_write16(®->control, ppi->ppi_control); - bfin_write16(®->count, samples_per_line - 1); - bfin_write16(®->frame, params->frame); - break; - } - case PPI_TYPE_EPPI: - { - struct bfin_eppi_regs *reg = info->base; - - if ((params->ppi_control & PACK_EN) - || (params->ppi_control & 0x38000) > DLEN_16) - dma32 = 1; - - bfin_write32(®->control, ppi->ppi_control); - bfin_write16(®->line, samples_per_line); - bfin_write16(®->frame, params->frame); - bfin_write16(®->hdelay, hdelay); - bfin_write16(®->vdelay, params->vdelay); - bfin_write16(®->hcount, hcount); - bfin_write16(®->vcount, params->height); - break; - } - case PPI_TYPE_EPPI3: - { - struct bfin_eppi3_regs *reg = info->base; - - if ((params->ppi_control & PACK_EN) - || (params->ppi_control & 0x70000) > DLEN_16) - dma32 = 1; - - bfin_write32(®->ctl, ppi->ppi_control); - bfin_write32(®->line, samples_per_line); - bfin_write32(®->frame, params->frame); - bfin_write32(®->hdly, hdelay); - bfin_write32(®->vdly, params->vdelay); - bfin_write32(®->hcnt, hcount); - bfin_write32(®->vcnt, params->height); - if (params->int_mask) - bfin_write32(®->imsk, params->int_mask & 0xFF); - if (ppi->ppi_control & PORT_DIR) { - u32 hsync_width, vsync_width, vsync_period; - - hsync_width = params->hsync - * params->bpp / params->dlen; - vsync_width = params->vsync * samples_per_line; - vsync_period = samples_per_line * params->frame; - bfin_write32(®->fs1_wlhb, hsync_width); - bfin_write32(®->fs1_paspl, samples_per_line); - bfin_write32(®->fs2_wlvb, vsync_width); - bfin_write32(®->fs2_palpf, vsync_period); - } - break; - } - default: - return -EINVAL; - } - - if (dma32) { - dma_config |= WDSIZE_32 | PSIZE_32; - set_dma_x_count(info->dma_ch, bytes_per_line >> 2); - set_dma_x_modify(info->dma_ch, 4); - set_dma_y_modify(info->dma_ch, 4); - } else { - dma_config |= WDSIZE_16 | PSIZE_16; - set_dma_x_count(info->dma_ch, bytes_per_line >> 1); - set_dma_x_modify(info->dma_ch, 2); - set_dma_y_modify(info->dma_ch, 2); - } - set_dma_y_count(info->dma_ch, params->height); - set_dma_config(info->dma_ch, dma_config); - - SSYNC(); - return 0; -} - -static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) -{ - set_dma_start_addr(ppi->info->dma_ch, addr); -} - -struct ppi_if *ppi_create_instance(struct platform_device *pdev, - const struct ppi_info *info) -{ - struct ppi_if *ppi; - - if (!info || !info->pin_req) - return NULL; - -#ifndef CONFIG_PINCTRL - if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { - dev_err(&pdev->dev, "request peripheral failed\n"); - return NULL; - } -#endif - - ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); - if (!ppi) { - peripheral_free_list(info->pin_req); - return NULL; - } - ppi->ops = &ppi_ops; - ppi->info = info; - ppi->dev = &pdev->dev; - - pr_info("ppi probe success\n"); - return ppi; -} -EXPORT_SYMBOL(ppi_create_instance); - -void ppi_delete_instance(struct ppi_if *ppi) -{ - peripheral_free_list(ppi->info->pin_req); - kfree(ppi); -} -EXPORT_SYMBOL(ppi_delete_instance); - -MODULE_DESCRIPTION("Analog Devices PPI driver"); -MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index 5debdf08fbe7..f1f28cf5c751 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -1,18 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2017 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 <linux/module.h> diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 9fe113cb901f..68ed2a564ad1 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -68,8 +68,9 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd) { struct coda_dev *dev = ctx->dev; - if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_960) { /* Restore context related registers to CODA */ coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); @@ -506,7 +507,8 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, goto err; } - if (!ctx->psbuf.vaddr && dev->devtype->product == CODA_7541) { + if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541)) { ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf"); if (ret < 0) @@ -594,6 +596,7 @@ static void coda_setup_iram(struct coda_ctx *ctx) int dbk_bits; int bit_bits; int ip_bits; + int me_bits; memset(iram_info, 0, sizeof(*iram_info)); iram_info->next_paddr = dev->iram.paddr; @@ -603,15 +606,23 @@ static void coda_setup_iram(struct coda_ctx *ctx) return; switch (dev->devtype->product) { + case CODA_HX4: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE; + break; case CODA_7541: dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE; break; case CODA_960: dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + me_bits = 0; break; default: /* CODA_DX6 */ return; @@ -626,7 +637,8 @@ static void coda_setup_iram(struct coda_ctx *ctx) w64 = mb_width * 64; /* Prioritize in case IRAM is too small for everything */ - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { iram_info->search_ram_size = round_up(mb_width * 16 * 36 + 2048, 1024); iram_info->search_ram_paddr = coda_iram_alloc(iram_info, @@ -635,8 +647,7 @@ static void coda_setup_iram(struct coda_ctx *ctx) pr_err("IRAM is smaller than the search ram size\n"); goto out; } - iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE | - CODA7_USE_ME_ENABLE; + iram_info->axi_sram_use |= me_bits; } /* Only H.264BP and H.263P3 are considered */ @@ -688,7 +699,8 @@ out: v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "IRAM smaller than needed\n"); - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { /* TODO - Enabling these causes picture errors on CODA7541 */ if (ctx->inst_type == CODA_INST_DECODER) { /* fw 1.4.50 */ @@ -706,6 +718,7 @@ out: static u32 coda_supported_firmwares[] = { CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), + CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), @@ -890,6 +903,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) case CODA_960: coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); /* fallthrough */ + case CODA_HX4: case CODA_7541: coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); @@ -919,6 +933,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; + case CODA_HX4: case CODA_7541: if (dst_fourcc == V4L2_PIX_FMT_H264) { value = (round_up(q_data_src->width, 16) & @@ -1086,6 +1101,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = FMO_SLICE_SAVE_BUF_SIZE << 7; coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); break; + case CODA_HX4: case CODA_7541: coda_write(dev, ctx->iram_info.search_ram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE); @@ -1131,7 +1147,8 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { coda_write(dev, q_data_src->bytesperline, CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); } @@ -1570,7 +1587,8 @@ static bool coda_reorder_enable(struct coda_ctx *ctx) struct coda_dev *dev = ctx->dev; int profile, level; - if (dev->devtype->product != CODA_7541 && + if (dev->devtype->product != CODA_HX4 && + dev->devtype->product != CODA_7541 && dev->devtype->product != CODA_960) return false; @@ -1664,7 +1682,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) CODA_CMD_DEC_SEQ_MP4_ASP_CLASS); } if (src_fourcc == V4L2_PIX_FMT_H264) { - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { coda_write(dev, ctx->psbuf.paddr, CODA_CMD_DEC_SEQ_PS_BB_START); coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), @@ -1791,7 +1810,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) CODA_CMD_SET_FRAME_SLICE_BB_SIZE); } - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { int max_mb_x = 1920 / 16; int max_mb_y = 1088 / 16; int max_mb_num = max_mb_x * max_mb_y; @@ -1909,6 +1929,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) switch (dev->devtype->product) { case CODA_DX6: /* TBD */ + case CODA_HX4: case CODA_7541: coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); break; @@ -2049,7 +2070,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "errors in %d macroblocks\n", err_mb); - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_HX4 || + dev->devtype->product == CODA_7541) { val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); if (val == 0) { /* not enough bitstream data */ diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index e8a7554a61d2..04e35d70ce2e 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -128,7 +128,8 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, /* * Arrays of codecs supported by each given version of Coda: * i.MX27 -> codadx6 - * i.MX5x -> coda7 + * i.MX51 -> codahx4 + * i.MX53 -> coda7 * i.MX6 -> coda960 * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants */ @@ -137,6 +138,13 @@ static const struct coda_codec codadx6_codecs[] = { CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), }; +static const struct coda_codec codahx4_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), +}; + static const struct coda_codec coda7_codecs[] = { CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), @@ -234,6 +242,11 @@ static const struct coda_video_device *codadx6_video_devices[] = { &coda_bit_encoder, }; +static const struct coda_video_device *codahx4_video_devices[] = { + &coda_bit_encoder, + &coda_bit_decoder, +}; + static const struct coda_video_device *coda7_video_devices[] = { &coda_bit_jpeg_encoder, &coda_bit_jpeg_decoder, @@ -332,6 +345,8 @@ const char *coda_product_name(int product) switch (product) { case CODA_DX6: return "CodaDx6"; + case CODA_HX4: + return "CodaHx4"; case CODA_7541: return "CODA7541"; case CODA_960: @@ -1775,7 +1790,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); - if (ctx->dev->devtype->product == CODA_7541) { + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) { v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_3_1, @@ -1803,7 +1819,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); - if (ctx->dev->devtype->product == CODA_7541 || + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541 || ctx->dev->devtype->product == CODA_960) { v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, @@ -2004,6 +2021,7 @@ static int coda_open(struct file *file) if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; /* fallthrough */ + case CODA_HX4: case CODA_7541: ctx->reg_idx = 0; break; @@ -2182,7 +2200,8 @@ static int coda_hw_init(struct coda_dev *dev) /* Tell the BIT where to find everything it needs */ if (dev->devtype->product == CODA_960 || - dev->devtype->product == CODA_7541) { + dev->devtype->product == CODA_7541 || + dev->devtype->product == CODA_HX4) { coda_write(dev, dev->tempbuf.paddr, CODA_REG_BIT_TEMP_BUF_ADDR); coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); @@ -2387,6 +2406,7 @@ put_pm: enum coda_platform { CODA_IMX27, + CODA_IMX51, CODA_IMX53, CODA_IMX6Q, CODA_IMX6DL, @@ -2407,6 +2427,21 @@ static const struct coda_devtype coda_devdata[] = { .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, .iram_size = 0xb000, }, + [CODA_IMX51] = { + .firmware = { + "vpu_fw_imx51.bin", + "vpu/vpu_fw_imx51.bin", + "v4l-codahx4-imx51.bin" + }, + .product = CODA_HX4, + .codecs = codahx4_codecs, + .num_codecs = ARRAY_SIZE(codahx4_codecs), + .vdevs = codahx4_video_devices, + .num_vdevs = ARRAY_SIZE(codahx4_video_devices), + .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, + }, [CODA_IMX53] = { .firmware = { "vpu_fw_imx53.bin", @@ -2463,6 +2498,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids); #ifdef CONFIG_OF static const struct of_device_id coda_dt_ids[] = { { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, + { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index c5f504d8cf67..c70cfab2433f 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -28,7 +28,7 @@ #include "coda_regs.h" -#define CODA_MAX_FRAMEBUFFERS 17 +#define CODA_MAX_FRAMEBUFFERS 19 #define FMO_SLICE_SAVE_BUF_SIZE (32) enum { @@ -43,6 +43,7 @@ enum coda_inst_type { enum coda_product { CODA_DX6 = 0xf001, + CODA_HX4 = 0xf00a, CODA_7541 = 0xf012, CODA_960 = 0xf020, }; diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index b73886519f4f..19cf6853411e 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -116,7 +116,7 @@ struct vpss_hw_ops { struct vpss_oper_config { __iomem void *vpss_regs_base0; __iomem void *vpss_regs_base1; - resource_size_t *vpss_regs_base2; + __iomem void *vpss_regs_base2; enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index ed9302caa004..a3cdac188190 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -670,10 +670,13 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, return; } if (target == V4L2_SEL_TGT_COMPOSE) { + u32 tmp_min_h = ffs(sink->width) - 3; + u32 tmp_min_v = ffs(sink->height) - 1; + if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; - max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); - max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1)); + max_sc_h = min(SCALER_MAX_HRATIO, 1 << tmp_min_h); + max_sc_v = min(SCALER_MAX_VRATIO, 1 << tmp_min_v); min_sz = var->min_out_pixsize; } else { u32 depth = fimc_get_format_depth(sink->fmt); diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index cfe4406a83ff..e0e291066037 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -159,7 +159,7 @@ void fimc_is_hw_load_setfile(struct fimc_is *is) int fimc_is_hw_change_mode(struct fimc_is *is) { - const u8 cmd[] = { + static const u8 cmd[] = { HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO, HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO, }; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 7b7250b1cff8..80670eeee142 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1443,24 +1443,24 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i) * the level which controls the number of read buffers. */ static int mcam_vidioc_g_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parms) + struct v4l2_streamparm *a) { struct mcam_camera *cam = video_drvdata(filp); int ret; - ret = sensor_call(cam, video, g_parm, parms); - parms->parm.capture.readbuffers = n_dma_bufs; + ret = v4l2_g_parm_cap(video_devdata(filp), cam->sensor, a); + a->parm.capture.readbuffers = n_dma_bufs; return ret; } static int mcam_vidioc_s_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parms) + struct v4l2_streamparm *a) { struct mcam_camera *cam = video_drvdata(filp); int ret; - ret = sensor_call(cam, video, s_parm, parms); - parms->parm.capture.readbuffers = n_dma_bufs; + ret = v4l2_s_parm_cap(video_devdata(filp), cam->sensor, a); + a->parm.capture.readbuffers = n_dma_bufs; return ret; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 843510979ad8..86f0a7134365 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1224,6 +1224,8 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) ctx->dpb_size = dpbsize; ctx->state = MTK_STATE_HEADER; mtk_v4l2_debug(1, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size); + + mtk_vdec_queue_res_chg_event(ctx); } static void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 123c2b26a933..1d8508237220 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -236,7 +236,6 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, struct dma_async_tx_descriptor *tx; enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; struct dma_chan *chan = vout->vrfb_dma_tx.chan; - struct dma_device *dmadev = chan->device; struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt; dma_cookie_t cookie; enum dma_status status; @@ -271,7 +270,7 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, xt->dst_sgl = true; xt->dst_inc = true; - tx = dmadev->device_prep_interleaved_dma(chan, xt, flags); + tx = dmaengine_prep_interleaved_dma(chan, xt, flags); if (tx == NULL) { pr_err("%s: DMA interleaved prep error\n", __func__); return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 1baf78d3c02d..bca894a00c07 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -106,8 +106,8 @@ int hfi_core_deinit(struct venus_core *core, bool blocking) if (!empty) { mutex_unlock(&core->lock); - wait_on_atomic_t(&core->insts_count, atomic_t_wait, - TASK_UNINTERRUPTIBLE); + wait_var_event(&core->insts_count, + !atomic_read(&core->insts_count)); mutex_lock(&core->lock); } @@ -229,8 +229,8 @@ void hfi_session_destroy(struct venus_inst *inst) mutex_lock(&core->lock); list_del_init(&inst->list); - atomic_dec(&core->insts_count); - wake_up_atomic_t(&core->insts_count); + if (atomic_dec_and_test(&core->insts_count)) + wake_up_var(&core->insts_count); mutex_unlock(&core->lock); } EXPORT_SYMBOL_GPL(hfi_session_destroy); diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index a681ae5381d6..90c93d9603dc 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -659,10 +659,10 @@ static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst, prop->buffer_type == HFI_BUFFER_OUTPUT2) { switch (prop->data[i]) { case HFI_BUFFER_MODE_STATIC: - inst->cap_bufs_mode_static = 1; + inst->cap_bufs_mode_static = true; break; case HFI_BUFFER_MODE_DYNAMIC: - inst->cap_bufs_mode_dynamic = 1; + inst->cap_bufs_mode_dynamic = true; break; default: break; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 23fdff7a7370..4a40e6ad1be7 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -168,12 +168,8 @@ static int rvin_setup(struct rvin_dev *vin) break; case V4L2_FIELD_ALTERNATE: case V4L2_FIELD_NONE: - if (vin->continuous) { - vnmc = VNMC_IM_ODD_EVEN; - progressive = true; - } else { - vnmc = VNMC_IM_ODD; - } + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; break; default: vnmc = VNMC_IM_ODD; @@ -298,14 +294,6 @@ static bool rvin_capture_active(struct rvin_dev *vin) return rvin_read(vin, VNMS_REG) & VNMS_CA; } -static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms) -{ - if (vin->continuous) - return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; - - return 0; -} - static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) { if (vin->format.field == V4L2_FIELD_ALTERNATE) { @@ -344,76 +332,47 @@ static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) rvin_write(vin, offset, VNMB_REG(slot)); } -/* Moves a buffer from the queue to the HW slots */ -static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +/* + * Moves a buffer from the queue to the HW slot. If no buffer is + * available use the scratch buffer. The scratch buffer is never + * returned to userspace, its only function is to enable the capture + * loop to keep running. + */ +static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) { struct rvin_buffer *buf; struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr_top; - - if (vin->queue_buf[slot] != NULL) - return true; + dma_addr_t phys_addr; - if (list_empty(&vin->buf_list)) - return false; + /* A already populated slot shall never be overwritten. */ + if (WARN_ON(vin->queue_buf[slot] != NULL)) + return; vin_dbg(vin, "Filling HW slot: %d\n", slot); - /* Keep track of buffer we give to HW */ - buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); - vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - vin->queue_buf[slot] = vbuf; - - /* Setup DMA */ - phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - rvin_set_slot_addr(vin, slot, phys_addr_top); - - return true; -} - -static bool rvin_fill_hw(struct rvin_dev *vin) -{ - int slot, limit; - - limit = vin->continuous ? HW_BUFFER_NUM : 1; - - for (slot = 0; slot < limit; slot++) - if (!rvin_fill_hw_slot(vin, slot)) - return false; - return true; -} - -static void rvin_capture_on(struct rvin_dev *vin) -{ - vin_dbg(vin, "Capture on in %s mode\n", - vin->continuous ? "continuous" : "single"); + if (list_empty(&vin->buf_list)) { + vin->queue_buf[slot] = NULL; + phys_addr = vin->scratch_phys; + } else { + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + } - if (vin->continuous) - /* Continuous Frame Capture Mode */ - rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - else - /* Single Frame Capture Mode */ - rvin_write(vin, VNFC_S_FRAME, VNFC_REG); + rvin_set_slot_addr(vin, slot, phys_addr); } static int rvin_capture_start(struct rvin_dev *vin) { - struct rvin_buffer *buf, *node; - int bufs, ret; - - /* Count number of free buffers */ - bufs = 0; - list_for_each_entry_safe(buf, node, &vin->buf_list, list) - bufs++; + int slot, ret; - /* Continuous capture requires more buffers then there are HW slots */ - vin->continuous = bufs > HW_BUFFER_NUM; - - if (!rvin_fill_hw(vin)) { - vin_err(vin, "HW not ready to start, not enough buffers available\n"); - return -EINVAL; - } + for (slot = 0; slot < HW_BUFFER_NUM; slot++) + rvin_fill_hw_slot(vin, slot); rvin_crop_scale_comp(vin); @@ -421,7 +380,10 @@ static int rvin_capture_start(struct rvin_dev *vin) if (ret) return ret; - rvin_capture_on(vin); + vin_dbg(vin, "Starting to capture\n"); + + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); vin->state = RUNNING; @@ -904,7 +866,7 @@ static irqreturn_t rvin_irq(int irq, void *data) struct rvin_dev *vin = data; u32 int_status, vnms; int slot; - unsigned int i, sequence, handled = 0; + unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&vin->qlock, flags); @@ -930,65 +892,25 @@ static irqreturn_t rvin_irq(int irq, void *data) /* Prepare for capture and update state */ vnms = rvin_read(vin, VNMS_REG); - slot = rvin_get_active_slot(vin, vnms); - sequence = vin->sequence++; - - vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", - sequence, slot, - slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0', - slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0', - slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0', - !list_empty(&vin->buf_list)); - - /* HW have written to a slot that is not prepared we are in trouble */ - if (WARN_ON((vin->queue_buf[slot] == NULL))) - goto done; + slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; /* Capture frame */ - vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); - vin->queue_buf[slot]->sequence = sequence; - vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); - vin->queue_buf[slot] = NULL; - - /* Prepare for next frame */ - if (!rvin_fill_hw(vin)) { - - /* - * Can't supply HW with new buffers fast enough. Halt - * capture until more buffers are available. - */ - vin->state = STALLED; - - /* - * The continuous capturing requires an explicit stop - * operation when there is no buffer to be set into - * the VnMBm registers. - */ - if (vin->continuous) { - rvin_capture_stop(vin); - vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence); - - /* Maybe we can continue in single capture mode */ - for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->queue_buf[i]) { - list_add(to_buf_list(vin->queue_buf[i]), - &vin->buf_list); - vin->queue_buf[i] = NULL; - } - } - - if (!list_empty(&vin->buf_list)) - rvin_capture_start(vin); - } + if (vin->queue_buf[slot]) { + vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); + vin->queue_buf[slot]->sequence = vin->sequence; + vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, + VB2_BUF_STATE_DONE); + vin->queue_buf[slot] = NULL; } else { - /* - * The single capturing requires an explicit capture - * operation to fetch the next frame. - */ - if (!vin->continuous) - rvin_capture_on(vin); + /* Scratch buffer was used, dropping frame. */ + vin_dbg(vin, "Dropping frame %u\n", vin->sequence); } + + vin->sequence++; + + /* Prepare for next frame */ + rvin_fill_hw_slot(vin, slot); done: spin_unlock_irqrestore(&vin->qlock, flags); @@ -1059,13 +981,6 @@ static void rvin_buffer_queue(struct vb2_buffer *vb) list_add_tail(to_buf_list(vbuf), &vin->buf_list); - /* - * If capture is stalled add buffer to HW and restart - * capturing if HW is ready to continue. - */ - if (vin->state == STALLED) - rvin_capture_start(vin); - spin_unlock_irqrestore(&vin->qlock, flags); } @@ -1076,6 +991,17 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; + /* Allocate scratch buffer. */ + vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage, + &vin->scratch_phys, GFP_KERNEL); + if (!vin->scratch) { + spin_lock_irqsave(&vin->qlock, flags); + return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&vin->qlock, flags); + vin_err(vin, "Failed to allocate scratch buffer\n"); + return -ENOMEM; + } + sd = vin_to_source(vin); v4l2_subdev_call(sd, video, s_stream, 1); @@ -1091,6 +1017,10 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) spin_unlock_irqrestore(&vin->qlock, flags); + if (ret) + dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, + vin->scratch_phys); + return ret; } @@ -1141,6 +1071,10 @@ static void rvin_stop_streaming(struct vb2_queue *vq) /* disable interrupts */ rvin_disable_interrupts(vin); + + /* Free scratch buffer. */ + dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, + vin->scratch_phys); } static const struct vb2_ops rvin_qops = { @@ -1189,7 +1123,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; + q->min_buffers_needed = 4; q->dev = vin->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 5382078143fb..95897127cc41 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -38,13 +38,11 @@ enum chip_id { /** * STOPPED - No operation in progress * RUNNING - Operation in progress have buffers - * STALLED - No operation in progress have no buffers * STOPPING - Stopping operation */ enum rvin_dma_state { STOPPED = 0, RUNNING, - STALLED, STOPPING, }; @@ -102,12 +100,13 @@ struct rvin_graph_entity { * * @lock: protects @queue * @queue: vb2 buffers queue + * @scratch: cpu address for scratch buffer + * @scratch_phys: physical address of the scratch buffer * - * @qlock: protects @queue_buf, @buf_list, @continuous, @sequence + * @qlock: protects @queue_buf, @buf_list, @sequence * @state * @queue_buf: Keeps track of buffers given to HW slot * @buf_list: list of queued buffers - * @continuous: tracks if active operation is continuous or single mode * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state * @@ -130,11 +129,12 @@ struct rvin_dev { struct mutex lock; struct vb2_queue queue; + void *scratch; + dma_addr_t scratch_phys; spinlock_t qlock; struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; struct list_head buf_list; - bool continuous; unsigned int sequence; enum rvin_dma_state state; diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index b2e080ef5391..dc7e280c91b4 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -274,7 +274,7 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr) { struct dma_slave_config dma_cfg; unsigned int i; - int ret = -ENODEV; + int ret; for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { struct rcar_drif *ch = sdr->ch[i]; @@ -282,6 +282,7 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr) ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx"); if (!ch->dmach) { rdrif_err(sdr, "ch%u: dma channel req failed\n", i); + ret = -ENODEV; goto dmach_error; } diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c new file mode 100644 index 000000000000..6599dba5ab84 --- /dev/null +++ b/drivers/media/platform/renesas-ceu.c @@ -0,0 +1,1677 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Driver for Renesas Capture Engine Unit (CEU) interface + * Copyright (C) 2017-2018 Jacopo Mondi <jacopo+renesas@jmondi.org> + * + * Based on soc-camera driver "soc_camera/sh_mobile_ceu_camera.c" + * Copyright (C) 2008 Magnus Damm + * + * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", + * Copyright (C) 2006, Sascha Hauer, Pengutronix + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/videodev2.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mediabus.h> +#include <media/videobuf2-dma-contig.h> + +#include <media/drv-intf/renesas-ceu.h> + +#define DRIVER_NAME "renesas-ceu" + +/* CEU registers offsets and masks. */ +#define CEU_CAPSR 0x00 /* Capture start register */ +#define CEU_CAPCR 0x04 /* Capture control register */ +#define CEU_CAMCR 0x08 /* Capture interface control register */ +#define CEU_CAMOR 0x10 /* Capture interface offset register */ +#define CEU_CAPWR 0x14 /* Capture interface width register */ +#define CEU_CAIFR 0x18 /* Capture interface input format register */ +#define CEU_CRCNTR 0x28 /* CEU register control register */ +#define CEU_CRCMPR 0x2c /* CEU register forcible control register */ +#define CEU_CFLCR 0x30 /* Capture filter control register */ +#define CEU_CFSZR 0x34 /* Capture filter size clip register */ +#define CEU_CDWDR 0x38 /* Capture destination width register */ +#define CEU_CDAYR 0x3c /* Capture data address Y register */ +#define CEU_CDACR 0x40 /* Capture data address C register */ +#define CEU_CFWCR 0x5c /* Firewall operation control register */ +#define CEU_CDOCR 0x64 /* Capture data output control register */ +#define CEU_CEIER 0x70 /* Capture event interrupt enable register */ +#define CEU_CETCR 0x74 /* Capture event flag clear register */ +#define CEU_CSTSR 0x7c /* Capture status register */ +#define CEU_CSRTR 0x80 /* Capture software reset register */ + +/* Data synchronous fetch mode. */ +#define CEU_CAMCR_JPEG BIT(4) + +/* Input components ordering: CEU_CAMCR.DTARY field. */ +#define CEU_CAMCR_DTARY_8_UYVY (0x00 << 8) +#define CEU_CAMCR_DTARY_8_VYUY (0x01 << 8) +#define CEU_CAMCR_DTARY_8_YUYV (0x02 << 8) +#define CEU_CAMCR_DTARY_8_YVYU (0x03 << 8) +/* TODO: input components ordering for 16 bits input. */ + +/* Bus transfer MTU. */ +#define CEU_CAPCR_BUS_WIDTH256 (0x3 << 20) + +/* Bus width configuration. */ +#define CEU_CAMCR_DTIF_16BITS BIT(12) + +/* No downsampling to planar YUV420 in image fetch mode. */ +#define CEU_CDOCR_NO_DOWSAMPLE BIT(4) + +/* Swap all input data in 8-bit, 16-bits and 32-bits units (Figure 46.45). */ +#define CEU_CDOCR_SWAP_ENDIANNESS (7) + +/* Capture reset and enable bits. */ +#define CEU_CAPSR_CPKIL BIT(16) +#define CEU_CAPSR_CE BIT(0) + +/* CEU operating flag bit. */ +#define CEU_CAPCR_CTNCP BIT(16) +#define CEU_CSTRST_CPTON BIT(0) + +/* Platform specific IRQ source flags. */ +#define CEU_CETCR_ALL_IRQS_RZ 0x397f313 +#define CEU_CETCR_ALL_IRQS_SH4 0x3d7f313 + +/* Prohibited register access interrupt bit. */ +#define CEU_CETCR_IGRW BIT(4) +/* One-frame capture end interrupt. */ +#define CEU_CEIER_CPE BIT(0) +/* VBP error. */ +#define CEU_CEIER_VBP BIT(20) +#define CEU_CEIER_MASK (CEU_CEIER_CPE | CEU_CEIER_VBP) + +#define CEU_MAX_WIDTH 2560 +#define CEU_MAX_HEIGHT 1920 +#define CEU_MAX_BPL 8188 +#define CEU_W_MAX(w) ((w) < CEU_MAX_WIDTH ? (w) : CEU_MAX_WIDTH) +#define CEU_H_MAX(h) ((h) < CEU_MAX_HEIGHT ? (h) : CEU_MAX_HEIGHT) + +/* + * ceu_bus_fmt - describe a 8-bits yuyv format the sensor can produce + * + * @mbus_code: bus format code + * @fmt_order: CEU_CAMCR.DTARY ordering of input components (Y, Cb, Cr) + * @fmt_order_swap: swapped CEU_CAMCR.DTARY ordering of input components + * (Y, Cr, Cb) + * @swapped: does Cr appear before Cb? + * @bps: number of bits sent over bus for each sample + * @bpp: number of bits per pixels unit + */ +struct ceu_mbus_fmt { + u32 mbus_code; + u32 fmt_order; + u32 fmt_order_swap; + bool swapped; + u8 bps; + u8 bpp; +}; + +/* + * ceu_buffer - Link vb2 buffer to the list of available buffers. + */ +struct ceu_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + +static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct ceu_buffer, vb); +} + +/* + * ceu_subdev - Wraps v4l2 sub-device and provides async subdevice. + */ +struct ceu_subdev { + struct v4l2_subdev *v4l2_sd; + struct v4l2_async_subdev asd; + + /* per-subdevice mbus configuration options */ + unsigned int mbus_flags; + struct ceu_mbus_fmt mbus_fmt; +}; + +static struct ceu_subdev *to_ceu_subdev(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct ceu_subdev, asd); +} + +/* + * ceu_device - CEU device instance + */ +struct ceu_device { + struct device *dev; + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* subdevices descriptors */ + struct ceu_subdev *subdevs; + /* the subdevice currently in use */ + struct ceu_subdev *sd; + unsigned int sd_index; + unsigned int num_sd; + + /* platform specific mask with all IRQ sources flagged */ + u32 irq_mask; + + /* currently configured field and pixel format */ + enum v4l2_field field; + struct v4l2_pix_format_mplane v4l2_pix; + + /* async subdev notification helpers */ + struct v4l2_async_notifier notifier; + /* pointers to "struct ceu_subdevice -> asd" */ + struct v4l2_async_subdev **asds; + + /* vb2 queue, capture buffer list and active buffer pointer */ + struct vb2_queue vb2_vq; + struct list_head capture; + struct vb2_v4l2_buffer *active; + unsigned int sequence; + + /* mlock - lock access to interface reset and vb2 queue */ + struct mutex mlock; + + /* lock - lock access to capture buffer queue and active buffer */ + spinlock_t lock; + + /* base - CEU memory base address */ + void __iomem *base; +}; + +static inline struct ceu_device *v4l2_to_ceu(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct ceu_device, v4l2_dev); +} + +/* --- CEU memory output formats --- */ + +/* + * ceu_fmt - describe a memory output format supported by CEU interface. + * + * @fourcc: memory layout fourcc format code + * @bpp: number of bits for each pixel stored in memory + */ +struct ceu_fmt { + u32 fourcc; + u32 bpp; +}; + +/* + * ceu_format_list - List of supported memory output formats + * + * If sensor provides any YUYV bus format, all the following planar memory + * formats are available thanks to CEU re-ordering and sub-sampling + * capabilities. + */ +static const struct ceu_fmt ceu_fmt_list[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = 12, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .bpp = 12, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = 16, + }, +}; + +static const struct ceu_fmt *get_ceu_fmt_from_fourcc(unsigned int fourcc) +{ + const struct ceu_fmt *fmt = &ceu_fmt_list[0]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ceu_fmt_list); i++, fmt++) + if (fmt->fourcc == fourcc) + return fmt; + + return NULL; +} + +static bool ceu_fmt_mplane(struct v4l2_pix_format_mplane *pix) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + return false; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return true; + default: + return false; + } +} + +/* --- CEU HW operations --- */ + +static void ceu_write(struct ceu_device *priv, unsigned int reg_offs, u32 data) +{ + iowrite32(data, priv->base + reg_offs); +} + +static u32 ceu_read(struct ceu_device *priv, unsigned int reg_offs) +{ + return ioread32(priv->base + reg_offs); +} + +/* + * ceu_soft_reset() - Software reset the CEU interface. + * @ceu_device: CEU device. + * + * Returns 0 for success, -EIO for error. + */ +static int ceu_soft_reset(struct ceu_device *ceudev) +{ + unsigned int i; + + ceu_write(ceudev, CEU_CAPSR, CEU_CAPSR_CPKIL); + + for (i = 0; i < 100; i++) { + if (!(ceu_read(ceudev, CEU_CSTSR) & CEU_CSTRST_CPTON)) + break; + udelay(1); + } + + if (i == 100) { + dev_err(ceudev->dev, "soft reset time out\n"); + return -EIO; + } + + for (i = 0; i < 100; i++) { + if (!(ceu_read(ceudev, CEU_CAPSR) & CEU_CAPSR_CPKIL)) + return 0; + udelay(1); + } + + /* If we get here, CEU has not reset properly. */ + return -EIO; +} + +/* --- CEU Capture Operations --- */ + +/* + * ceu_hw_config() - Configure CEU interface registers. + */ +static int ceu_hw_config(struct ceu_device *ceudev) +{ + u32 camcr, cdocr, cfzsr, cdwdr, capwr; + struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix; + struct ceu_subdev *ceu_sd = ceudev->sd; + struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt; + unsigned int mbus_flags = ceu_sd->mbus_flags; + + /* Start configuring CEU registers */ + ceu_write(ceudev, CEU_CAIFR, 0); + ceu_write(ceudev, CEU_CFWCR, 0); + ceu_write(ceudev, CEU_CRCNTR, 0); + ceu_write(ceudev, CEU_CRCMPR, 0); + + /* Set the frame capture period for both image capture and data sync. */ + capwr = (pix->height << 16) | pix->width * mbus_fmt->bpp / 8; + + /* + * Swap input data endianness by default. + * In data fetch mode bytes are received in chunks of 8 bytes. + * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first) + * The data is however by default written to memory in reverse order: + * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte) + * + * Use CEU_CDOCR[2:0] to swap data ordering. + */ + cdocr = CEU_CDOCR_SWAP_ENDIANNESS; + + /* + * Configure CAMCR and CDOCR: + * match input components ordering with memory output format and + * handle downsampling to YUV420. + * + * If the memory output planar format is 'swapped' (Cr before Cb) and + * input format is not, use the swapped version of CAMCR.DTARY. + * + * If the memory output planar format is not 'swapped' (Cb before Cr) + * and input format is, use the swapped version of CAMCR.DTARY. + * + * CEU by default downsample to planar YUV420 (CDCOR[4] = 0). + * If output is planar YUV422 set CDOCR[4] = 1 + * + * No downsample for data fetch sync mode. + */ + switch (pix->pixelformat) { + /* Data fetch sync mode */ + case V4L2_PIX_FMT_YUYV: + /* TODO: handle YUYV permutations through DTARY bits. */ + camcr = CEU_CAMCR_JPEG; + cdocr |= CEU_CDOCR_NO_DOWSAMPLE; + cfzsr = (pix->height << 16) | pix->width; + cdwdr = pix->plane_fmt[0].bytesperline; + break; + + /* Non-swapped planar image capture mode. */ + case V4L2_PIX_FMT_NV16: + cdocr |= CEU_CDOCR_NO_DOWSAMPLE; + /* fall-through */ + case V4L2_PIX_FMT_NV12: + if (mbus_fmt->swapped) + camcr = mbus_fmt->fmt_order_swap; + else + camcr = mbus_fmt->fmt_order; + + cfzsr = (pix->height << 16) | pix->width; + cdwdr = pix->width; + break; + + /* Swapped planar image capture mode. */ + case V4L2_PIX_FMT_NV61: + cdocr |= CEU_CDOCR_NO_DOWSAMPLE; + /* fall-through */ + case V4L2_PIX_FMT_NV21: + if (mbus_fmt->swapped) + camcr = mbus_fmt->fmt_order; + else + camcr = mbus_fmt->fmt_order_swap; + + cfzsr = (pix->height << 16) | pix->width; + cdwdr = pix->width; + break; + + default: + return -EINVAL; + } + + camcr |= mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; + camcr |= mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; + + /* TODO: handle 16 bit bus width with DTIF bit in CAMCR */ + ceu_write(ceudev, CEU_CAMCR, camcr); + ceu_write(ceudev, CEU_CDOCR, cdocr); + ceu_write(ceudev, CEU_CAPCR, CEU_CAPCR_BUS_WIDTH256); + + /* + * TODO: make CAMOR offsets configurable. + * CAMOR wants to know the number of blanks between a VS/HS signal + * and valid data. This value should actually come from the sensor... + */ + ceu_write(ceudev, CEU_CAMOR, 0); + + /* TODO: 16 bit bus width require re-calculation of cdwdr and cfzsr */ + ceu_write(ceudev, CEU_CAPWR, capwr); + ceu_write(ceudev, CEU_CFSZR, cfzsr); + ceu_write(ceudev, CEU_CDWDR, cdwdr); + + return 0; +} + +/* + * ceu_capture() - Trigger start of a capture sequence. + * + * Program the CEU DMA registers with addresses where to transfer image data. + */ +static int ceu_capture(struct ceu_device *ceudev) +{ + struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix; + dma_addr_t phys_addr_top; + + phys_addr_top = + vb2_dma_contig_plane_dma_addr(&ceudev->active->vb2_buf, 0); + ceu_write(ceudev, CEU_CDAYR, phys_addr_top); + + /* Ignore CbCr plane for non multi-planar image formats. */ + if (ceu_fmt_mplane(pix)) { + phys_addr_top = + vb2_dma_contig_plane_dma_addr(&ceudev->active->vb2_buf, + 1); + ceu_write(ceudev, CEU_CDACR, phys_addr_top); + } + + /* + * Trigger new capture start: once for each frame, as we work in + * one-frame capture mode. + */ + ceu_write(ceudev, CEU_CAPSR, CEU_CAPSR_CE); + + return 0; +} + +static irqreturn_t ceu_irq(int irq, void *data) +{ + struct ceu_device *ceudev = data; + struct vb2_v4l2_buffer *vbuf; + struct ceu_buffer *buf; + u32 status; + + /* Clean interrupt status. */ + status = ceu_read(ceudev, CEU_CETCR); + ceu_write(ceudev, CEU_CETCR, ~ceudev->irq_mask); + + /* Unexpected interrupt. */ + if (!(status & CEU_CEIER_MASK)) + return IRQ_NONE; + + spin_lock(&ceudev->lock); + + /* Stale interrupt from a released buffer, ignore it. */ + vbuf = ceudev->active; + if (!vbuf) { + spin_unlock(&ceudev->lock); + return IRQ_HANDLED; + } + + /* + * When a VBP interrupt occurs, no capture end interrupt will occur + * and the image of that frame is not captured correctly. + */ + if (status & CEU_CEIER_VBP) { + dev_err(ceudev->dev, "VBP interrupt: abort capture\n"); + goto error_irq_out; + } + + /* Prepare to return the 'previous' buffer. */ + vbuf->vb2_buf.timestamp = ktime_get_ns(); + vbuf->sequence = ceudev->sequence++; + vbuf->field = ceudev->field; + + /* Prepare a new 'active' buffer and trigger a new capture. */ + if (!list_empty(&ceudev->capture)) { + buf = list_first_entry(&ceudev->capture, struct ceu_buffer, + queue); + list_del(&buf->queue); + ceudev->active = &buf->vb; + + ceu_capture(ceudev); + } + + /* Return the 'previous' buffer. */ + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); + + spin_unlock(&ceudev->lock); + + return IRQ_HANDLED; + +error_irq_out: + /* Return the 'previous' buffer and all queued ones. */ + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR); + + list_for_each_entry(buf, &ceudev->capture, queue) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + + spin_unlock(&ceudev->lock); + + return IRQ_HANDLED; +} + +/* --- CEU Videobuf2 operations --- */ + +static void ceu_update_plane_sizes(struct v4l2_plane_pix_format *plane, + unsigned int bpl, unsigned int szimage) +{ + memset(plane, 0, sizeof(*plane)); + + plane->sizeimage = szimage; + if (plane->bytesperline < bpl || plane->bytesperline > CEU_MAX_BPL) + plane->bytesperline = bpl; +} + +/* + * ceu_calc_plane_sizes() - Fill per-plane 'struct v4l2_plane_pix_format' + * information according to the currently configured + * pixel format. + * @ceu_device: CEU device. + * @ceu_fmt: Active image format. + * @pix: Pixel format information (store line width and image sizes) + */ +static void ceu_calc_plane_sizes(struct ceu_device *ceudev, + const struct ceu_fmt *ceu_fmt, + struct v4l2_pix_format_mplane *pix) +{ + unsigned int bpl, szimage; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + pix->num_planes = 1; + bpl = pix->width * ceu_fmt->bpp / 8; + szimage = pix->height * bpl; + ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage); + break; + + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + pix->num_planes = 2; + bpl = pix->width; + szimage = pix->height * pix->width; + ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage); + ceu_update_plane_sizes(&pix->plane_fmt[1], bpl, szimage / 2); + break; + + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + default: + pix->num_planes = 2; + bpl = pix->width; + szimage = pix->height * pix->width; + ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage); + ceu_update_plane_sizes(&pix->plane_fmt[1], bpl, szimage); + break; + } +} + +/* + * ceu_vb2_setup() - is called to check whether the driver can accept the + * requested number of buffers and to fill in plane sizes + * for the current frame format, if required. + */ +static int ceu_vb2_setup(struct vb2_queue *vq, unsigned int *count, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct ceu_device *ceudev = vb2_get_drv_priv(vq); + struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix; + unsigned int i; + + /* num_planes is set: just check plane sizes. */ + if (*num_planes) { + for (i = 0; i < pix->num_planes; i++) + if (sizes[i] < pix->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + /* num_planes not set: called from REQBUFS, just set plane sizes. */ + *num_planes = pix->num_planes; + for (i = 0; i < pix->num_planes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + + return 0; +} + +static void ceu_vb2_queue(struct vb2_buffer *vb) +{ + struct ceu_device *ceudev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct ceu_buffer *buf = vb2_to_ceu(vbuf); + unsigned long irqflags; + + spin_lock_irqsave(&ceudev->lock, irqflags); + list_add_tail(&buf->queue, &ceudev->capture); + spin_unlock_irqrestore(&ceudev->lock, irqflags); +} + +static int ceu_vb2_prepare(struct vb2_buffer *vb) +{ + struct ceu_device *ceudev = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix; + unsigned int i; + + for (i = 0; i < pix->num_planes; i++) { + if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { + dev_err(ceudev->dev, + "Plane size too small (%lu < %u)\n", + vb2_plane_size(vb, i), + pix->plane_fmt[i].sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); + } + + return 0; +} + +static int ceu_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct ceu_device *ceudev = vb2_get_drv_priv(vq); + struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd; + struct ceu_buffer *buf; + unsigned long irqflags; + int ret; + + /* Program the CEU interface according to the CEU image format. */ + ret = ceu_hw_config(ceudev); + if (ret) + goto error_return_bufs; + + ret = v4l2_subdev_call(v4l2_sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + dev_dbg(ceudev->dev, + "Subdevice failed to start streaming: %d\n", ret); + goto error_return_bufs; + } + + spin_lock_irqsave(&ceudev->lock, irqflags); + ceudev->sequence = 0; + + /* Grab the first available buffer and trigger the first capture. */ + buf = list_first_entry(&ceudev->capture, struct ceu_buffer, + queue); + if (!buf) { + spin_unlock_irqrestore(&ceudev->lock, irqflags); + dev_dbg(ceudev->dev, + "No buffer available for capture.\n"); + goto error_stop_sensor; + } + + list_del(&buf->queue); + ceudev->active = &buf->vb; + + /* Clean and program interrupts for first capture. */ + ceu_write(ceudev, CEU_CETCR, ~ceudev->irq_mask); + ceu_write(ceudev, CEU_CEIER, CEU_CEIER_MASK); + + ceu_capture(ceudev); + + spin_unlock_irqrestore(&ceudev->lock, irqflags); + + return 0; + +error_stop_sensor: + v4l2_subdev_call(v4l2_sd, video, s_stream, 0); + +error_return_bufs: + spin_lock_irqsave(&ceudev->lock, irqflags); + list_for_each_entry(buf, &ceudev->capture, queue) + vb2_buffer_done(&ceudev->active->vb2_buf, + VB2_BUF_STATE_QUEUED); + ceudev->active = NULL; + spin_unlock_irqrestore(&ceudev->lock, irqflags); + + return ret; +} + +static void ceu_stop_streaming(struct vb2_queue *vq) +{ + struct ceu_device *ceudev = vb2_get_drv_priv(vq); + struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd; + struct ceu_buffer *buf; + unsigned long irqflags; + + /* Clean and disable interrupt sources. */ + ceu_write(ceudev, CEU_CETCR, + ceu_read(ceudev, CEU_CETCR) & ceudev->irq_mask); + ceu_write(ceudev, CEU_CEIER, CEU_CEIER_MASK); + + v4l2_subdev_call(v4l2_sd, video, s_stream, 0); + + spin_lock_irqsave(&ceudev->lock, irqflags); + if (ceudev->active) { + vb2_buffer_done(&ceudev->active->vb2_buf, + VB2_BUF_STATE_ERROR); + ceudev->active = NULL; + } + + /* Release all queued buffers. */ + list_for_each_entry(buf, &ceudev->capture, queue) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&ceudev->capture); + + spin_unlock_irqrestore(&ceudev->lock, irqflags); + + ceu_soft_reset(ceudev); +} + +static const struct vb2_ops ceu_vb2_ops = { + .queue_setup = ceu_vb2_setup, + .buf_queue = ceu_vb2_queue, + .buf_prepare = ceu_vb2_prepare, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = ceu_start_streaming, + .stop_streaming = ceu_stop_streaming, +}; + +/* --- CEU image formats handling --- */ + +/* + * ceu_try_fmt() - test format on CEU and sensor + * @ceudev: The CEU device. + * @v4l2_fmt: format to test. + * + * Returns 0 for success, < 0 for errors. + */ +static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +{ + struct ceu_subdev *ceu_sd = ceudev->sd; + struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; + struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + struct v4l2_subdev_pad_config pad_cfg; + const struct ceu_fmt *ceu_fmt; + int ret; + + struct v4l2_subdev_format sd_format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + break; + + default: + pix->pixelformat = V4L2_PIX_FMT_NV16; + break; + } + + ceu_fmt = get_ceu_fmt_from_fourcc(pix->pixelformat); + + /* CFSZR requires height and width to be 4-pixel aligned. */ + v4l_bound_align_image(&pix->width, 2, CEU_MAX_WIDTH, 4, + &pix->height, 4, CEU_MAX_HEIGHT, 4, 0); + + /* + * Set format on sensor sub device: bus format used to produce memory + * format is selected at initialization time. + */ + v4l2_fill_mbus_format_mplane(&sd_format.format, pix); + ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format); + if (ret) + return ret; + + /* Apply size returned by sensor as the CEU can't scale. */ + v4l2_fill_pix_format_mplane(pix, &sd_format.format); + + /* Calculate per-plane sizes based on image format. */ + ceu_calc_plane_sizes(ceudev, ceu_fmt, pix); + + return 0; +} + +/* + * ceu_set_fmt() - Apply the supplied format to both sensor and CEU + */ +static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +{ + struct ceu_subdev *ceu_sd = ceudev->sd; + struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + int ret; + + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + ret = ceu_try_fmt(ceudev, v4l2_fmt); + if (ret) + return ret; + + v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp); + ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format); + if (ret) + return ret; + + ceudev->v4l2_pix = v4l2_fmt->fmt.pix_mp; + ceudev->field = V4L2_FIELD_NONE; + + return 0; +} + +/* + * ceu_set_default_fmt() - Apply default NV16 memory output format with VGA + * sizes. + */ +static int ceu_set_default_fmt(struct ceu_device *ceudev) +{ + int ret; + + struct v4l2_format v4l2_fmt = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .fmt.pix_mp = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .field = V4L2_FIELD_NONE, + .pixelformat = V4L2_PIX_FMT_NV16, + .num_planes = 2, + .plane_fmt = { + [0] = { + .sizeimage = VGA_WIDTH * VGA_HEIGHT * 2, + .bytesperline = VGA_WIDTH * 2, + }, + [1] = { + .sizeimage = VGA_WIDTH * VGA_HEIGHT * 2, + .bytesperline = VGA_WIDTH * 2, + }, + }, + }, + }; + + ret = ceu_try_fmt(ceudev, &v4l2_fmt); + if (ret) + return ret; + + ceudev->v4l2_pix = v4l2_fmt.fmt.pix_mp; + ceudev->field = V4L2_FIELD_NONE; + + return 0; +} + +/* + * ceu_init_mbus_fmt() - Query sensor for supported formats and initialize + * CEU media bus format used to produce memory formats. + * + * Find out if sensor can produce a permutation of 8-bits YUYV bus format. + * From a single 8-bits YUYV bus format the CEU can produce several memory + * output formats: + * - NV[12|21|16|61] through image fetch mode; + * - YUYV422 if sensor provides YUYV422 + * + * TODO: Other YUYV422 permutations through data fetch sync mode and DTARY + * TODO: Binary data (eg. JPEG) and raw formats through data fetch sync mode + */ +static int ceu_init_mbus_fmt(struct ceu_device *ceudev) +{ + struct ceu_subdev *ceu_sd = ceudev->sd; + struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt; + struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + bool yuyv_bus_fmt = false; + + struct v4l2_subdev_mbus_code_enum sd_mbus_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = 0, + }; + + /* Find out if sensor can produce any permutation of 8-bits YUYV422. */ + while (!yuyv_bus_fmt && + !v4l2_subdev_call(v4l2_sd, pad, enum_mbus_code, + NULL, &sd_mbus_fmt)) { + switch (sd_mbus_fmt.code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + yuyv_bus_fmt = true; + break; + default: + /* + * Only support 8-bits YUYV bus formats at the moment; + * + * TODO: add support for binary formats (data sync + * fetch mode). + */ + break; + } + + sd_mbus_fmt.index++; + } + + if (!yuyv_bus_fmt) + return -ENXIO; + + /* + * Save the first encountered YUYV format as "mbus_fmt" and use it + * to output all planar YUV422 and YUV420 (NV*) formats to memory as + * well as for data synch fetch mode (YUYV - YVYU etc. ). + */ + mbus_fmt->mbus_code = sd_mbus_fmt.code; + mbus_fmt->bps = 8; + + /* Annotate the selected bus format components ordering. */ + switch (sd_mbus_fmt.code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YUYV; + mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YVYU; + mbus_fmt->swapped = false; + mbus_fmt->bpp = 16; + break; + + case MEDIA_BUS_FMT_YVYU8_2X8: + mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YVYU; + mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YUYV; + mbus_fmt->swapped = true; + mbus_fmt->bpp = 16; + break; + + case MEDIA_BUS_FMT_UYVY8_2X8: + mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_UYVY; + mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_VYUY; + mbus_fmt->swapped = false; + mbus_fmt->bpp = 16; + break; + + case MEDIA_BUS_FMT_VYUY8_2X8: + mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_VYUY; + mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_UYVY; + mbus_fmt->swapped = true; + mbus_fmt->bpp = 16; + break; + } + + return 0; +} + +/* --- Runtime PM Handlers --- */ + +/* + * ceu_runtime_resume() - soft-reset the interface and turn sensor power on. + */ +static int __maybe_unused ceu_runtime_resume(struct device *dev) +{ + struct ceu_device *ceudev = dev_get_drvdata(dev); + struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd; + + v4l2_subdev_call(v4l2_sd, core, s_power, 1); + + ceu_soft_reset(ceudev); + + return 0; +} + +/* + * ceu_runtime_suspend() - disable capture and interrupts and soft-reset. + * Turn sensor power off. + */ +static int __maybe_unused ceu_runtime_suspend(struct device *dev) +{ + struct ceu_device *ceudev = dev_get_drvdata(dev); + struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd; + + v4l2_subdev_call(v4l2_sd, core, s_power, 0); + + ceu_write(ceudev, CEU_CEIER, 0); + ceu_soft_reset(ceudev); + + return 0; +} + +/* --- File Operations --- */ + +static int ceu_open(struct file *file) +{ + struct ceu_device *ceudev = video_drvdata(file); + int ret; + + ret = v4l2_fh_open(file); + if (ret) + return ret; + + mutex_lock(&ceudev->mlock); + /* Causes soft-reset and sensor power on on first open */ + pm_runtime_get_sync(ceudev->dev); + mutex_unlock(&ceudev->mlock); + + return 0; +} + +static int ceu_release(struct file *file) +{ + struct ceu_device *ceudev = video_drvdata(file); + + vb2_fop_release(file); + + mutex_lock(&ceudev->mlock); + /* Causes soft-reset and sensor power down on last close */ + pm_runtime_put(ceudev->dev); + mutex_unlock(&ceudev->mlock); + + return 0; +} + +static const struct v4l2_file_operations ceu_fops = { + .owner = THIS_MODULE, + .open = ceu_open, + .release = ceu_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* --- Video Device IOCTLs --- */ + +static int ceu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct ceu_device *ceudev = video_drvdata(file); + + strlcpy(cap->card, "Renesas CEU", sizeof(cap->card)); + strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:renesas-ceu-%s", dev_name(ceudev->dev)); + + return 0; +} + +static int ceu_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct ceu_fmt *fmt; + + if (f->index >= ARRAY_SIZE(ceu_fmt_list)) + return -EINVAL; + + fmt = &ceu_fmt_list[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int ceu_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ceu_device *ceudev = video_drvdata(file); + + return ceu_try_fmt(ceudev, f); +} + +static int ceu_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ceu_device *ceudev = video_drvdata(file); + + if (vb2_is_streaming(&ceudev->vb2_vq)) + return -EBUSY; + + return ceu_set_fmt(ceudev, f); +} + +static int ceu_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ceu_device *ceudev = video_drvdata(file); + + f->fmt.pix_mp = ceudev->v4l2_pix; + + return 0; +} + +static int ceu_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct ceu_device *ceudev = video_drvdata(file); + struct ceu_subdev *ceusd; + + if (inp->index >= ceudev->num_sd) + return -EINVAL; + + ceusd = &ceudev->subdevs[inp->index]; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = 0; + snprintf(inp->name, sizeof(inp->name), "Camera%u: %s", + inp->index, ceusd->v4l2_sd->name); + + return 0; +} + +static int ceu_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct ceu_device *ceudev = video_drvdata(file); + + *i = ceudev->sd_index; + + return 0; +} + +static int ceu_s_input(struct file *file, void *priv, unsigned int i) +{ + struct ceu_device *ceudev = video_drvdata(file); + struct ceu_subdev *ceu_sd_old; + int ret; + + if (i >= ceudev->num_sd) + return -EINVAL; + + if (vb2_is_streaming(&ceudev->vb2_vq)) + return -EBUSY; + + if (i == ceudev->sd_index) + return 0; + + ceu_sd_old = ceudev->sd; + ceudev->sd = &ceudev->subdevs[i]; + + /* + * Make sure we can generate output image formats and apply + * default one. + */ + ret = ceu_init_mbus_fmt(ceudev); + if (ret) { + ceudev->sd = ceu_sd_old; + return -EINVAL; + } + + ret = ceu_set_default_fmt(ceudev); + if (ret) { + ceudev->sd = ceu_sd_old; + return -EINVAL; + } + + /* Now that we're sure we can use the sensor, power off the old one. */ + v4l2_subdev_call(ceu_sd_old->v4l2_sd, core, s_power, 0); + v4l2_subdev_call(ceudev->sd->v4l2_sd, core, s_power, 1); + + ceudev->sd_index = i; + + return 0; +} + +static int ceu_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct ceu_device *ceudev = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), ceudev->sd->v4l2_sd, a); +} + +static int ceu_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct ceu_device *ceudev = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), ceudev->sd->v4l2_sd, a); +} + +static int ceu_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct ceu_device *ceudev = video_drvdata(file); + struct ceu_subdev *ceu_sd = ceudev->sd; + const struct ceu_fmt *ceu_fmt; + struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + int ret; + + struct v4l2_subdev_frame_size_enum fse = { + .code = ceu_sd->mbus_fmt.mbus_code, + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + /* Just check if user supplied pixel format is supported. */ + ceu_fmt = get_ceu_fmt_from_fourcc(fsize->pixel_format); + if (!ceu_fmt) + return -EINVAL; + + ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size, + NULL, &fse); + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = CEU_W_MAX(fse.max_width); + fsize->discrete.height = CEU_H_MAX(fse.max_height); + + return 0; +} + +static int ceu_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct ceu_device *ceudev = video_drvdata(file); + struct ceu_subdev *ceu_sd = ceudev->sd; + const struct ceu_fmt *ceu_fmt; + struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + int ret; + + struct v4l2_subdev_frame_interval_enum fie = { + .code = ceu_sd->mbus_fmt.mbus_code, + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + /* Just check if user supplied pixel format is supported. */ + ceu_fmt = get_ceu_fmt_from_fourcc(fival->pixel_format); + if (!ceu_fmt) + return -EINVAL; + + ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL, + &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_ioctl_ops ceu_ioctl_ops = { + .vidioc_querycap = ceu_querycap, + + .vidioc_enum_fmt_vid_cap_mplane = ceu_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = ceu_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = ceu_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = ceu_g_fmt_vid_cap, + + .vidioc_enum_input = ceu_enum_input, + .vidioc_g_input = ceu_g_input, + .vidioc_s_input = ceu_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_parm = ceu_g_parm, + .vidioc_s_parm = ceu_s_parm, + .vidioc_enum_framesizes = ceu_enum_framesizes, + .vidioc_enum_frameintervals = ceu_enum_frameintervals, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * ceu_vdev_release() - release CEU video device memory when last reference + * to this driver is closed + */ +static void ceu_vdev_release(struct video_device *vdev) +{ + struct ceu_device *ceudev = video_get_drvdata(vdev); + + kfree(ceudev); +} + +static int ceu_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *v4l2_sd, + struct v4l2_async_subdev *asd) +{ + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev); + struct ceu_subdev *ceu_sd = to_ceu_subdev(asd); + + ceu_sd->v4l2_sd = v4l2_sd; + ceudev->num_sd++; + + return 0; +} + +static int ceu_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev); + struct video_device *vdev = &ceudev->vdev; + struct vb2_queue *q = &ceudev->vb2_vq; + struct v4l2_subdev *v4l2_sd; + int ret; + + /* Initialize vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = ceudev; + q->ops = &ceu_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct ceu_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &ceudev->mlock; + q->dev = ceudev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* + * Make sure at least one sensor is primary and use it to initialize + * ceu formats. + */ + if (!ceudev->sd) { + ceudev->sd = &ceudev->subdevs[0]; + ceudev->sd_index = 0; + } + + v4l2_sd = ceudev->sd->v4l2_sd; + + ret = ceu_init_mbus_fmt(ceudev); + if (ret) + return ret; + + ret = ceu_set_default_fmt(ceudev); + if (ret) + return ret; + + /* Register the video device. */ + strncpy(vdev->name, DRIVER_NAME, strlen(DRIVER_NAME)); + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &ceudev->mlock; + vdev->queue = &ceudev->vb2_vq; + vdev->ctrl_handler = v4l2_sd->ctrl_handler; + vdev->fops = &ceu_fops; + vdev->ioctl_ops = &ceu_ioctl_ops; + vdev->release = ceu_vdev_release; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_STREAMING; + video_set_drvdata(vdev, ceudev); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + v4l2_err(vdev->v4l2_dev, + "video_register_device failed: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations ceu_notify_ops = { + .bound = ceu_notify_bound, + .complete = ceu_notify_complete, +}; + +/* + * ceu_init_async_subdevs() - Initialize CEU subdevices and async_subdevs in + * ceu device. Both DT and platform data parsing use + * this routine. + * + * Returns 0 for success, -ENOMEM for failure. + */ +static int ceu_init_async_subdevs(struct ceu_device *ceudev, unsigned int n_sd) +{ + /* Reserve memory for 'n_sd' ceu_subdev descriptors. */ + ceudev->subdevs = devm_kcalloc(ceudev->dev, n_sd, + sizeof(*ceudev->subdevs), GFP_KERNEL); + if (!ceudev->subdevs) + return -ENOMEM; + + /* + * Reserve memory for 'n_sd' pointers to async_subdevices. + * ceudev->asds members will point to &ceu_subdev.asd + */ + ceudev->asds = devm_kcalloc(ceudev->dev, n_sd, + sizeof(*ceudev->asds), GFP_KERNEL); + if (!ceudev->asds) + return -ENOMEM; + + ceudev->sd = NULL; + ceudev->sd_index = 0; + ceudev->num_sd = 0; + + return 0; +} + +/* + * ceu_parse_platform_data() - Initialize async_subdevices using platform + * device provided data. + */ +static int ceu_parse_platform_data(struct ceu_device *ceudev, + const struct ceu_platform_data *pdata) +{ + const struct ceu_async_subdev *async_sd; + struct ceu_subdev *ceu_sd; + unsigned int i; + int ret; + + if (pdata->num_subdevs == 0) + return -ENODEV; + + ret = ceu_init_async_subdevs(ceudev, pdata->num_subdevs); + if (ret) + return ret; + + for (i = 0; i < pdata->num_subdevs; i++) { + /* Setup the ceu subdevice and the async subdevice. */ + async_sd = &pdata->subdevs[i]; + ceu_sd = &ceudev->subdevs[i]; + + INIT_LIST_HEAD(&ceu_sd->asd.list); + + ceu_sd->mbus_flags = async_sd->flags; + ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_I2C; + ceu_sd->asd.match.i2c.adapter_id = async_sd->i2c_adapter_id; + ceu_sd->asd.match.i2c.address = async_sd->i2c_address; + + ceudev->asds[i] = &ceu_sd->asd; + } + + return pdata->num_subdevs; +} + +/* + * ceu_parse_dt() - Initialize async_subdevs parsing device tree graph. + */ +static int ceu_parse_dt(struct ceu_device *ceudev) +{ + struct device_node *of = ceudev->dev->of_node; + struct v4l2_fwnode_endpoint fw_ep; + struct ceu_subdev *ceu_sd; + struct device_node *ep; + unsigned int i; + int num_ep; + int ret; + + num_ep = of_graph_get_endpoint_count(of); + if (!num_ep) + return -ENODEV; + + ret = ceu_init_async_subdevs(ceudev, num_ep); + if (ret) + return ret; + + for (i = 0; i < num_ep; i++) { + ep = of_graph_get_endpoint_by_regs(of, 0, i); + if (!ep) { + dev_err(ceudev->dev, + "No subdevice connected on endpoint %u.\n", i); + ret = -ENODEV; + goto error_put_node; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep); + if (ret) { + dev_err(ceudev->dev, + "Unable to parse endpoint #%u.\n", i); + goto error_put_node; + } + + if (fw_ep.bus_type != V4L2_MBUS_PARALLEL) { + dev_err(ceudev->dev, + "Only parallel input supported.\n"); + ret = -EINVAL; + goto error_put_node; + } + + /* Setup the ceu subdevice and the async subdevice. */ + ceu_sd = &ceudev->subdevs[i]; + INIT_LIST_HEAD(&ceu_sd->asd.list); + + ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; + ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + ceu_sd->asd.match.fwnode = + fwnode_graph_get_remote_port_parent( + of_fwnode_handle(ep)); + + ceudev->asds[i] = &ceu_sd->asd; + of_node_put(ep); + } + + return num_ep; + +error_put_node: + of_node_put(ep); + return ret; +} + +/* + * struct ceu_data - Platform specific CEU data + * @irq_mask: CETCR mask with all interrupt sources enabled. The mask differs + * between SH4 and RZ platforms. + */ +struct ceu_data { + u32 irq_mask; +}; + +static const struct ceu_data ceu_data_rz = { + .irq_mask = CEU_CETCR_ALL_IRQS_RZ, +}; + +static const struct ceu_data ceu_data_sh4 = { + .irq_mask = CEU_CETCR_ALL_IRQS_SH4, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ceu_of_match[] = { + { .compatible = "renesas,r7s72100-ceu", .data = &ceu_data_rz }, + { } +}; +MODULE_DEVICE_TABLE(of, ceu_of_match); +#endif + +static int ceu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct ceu_data *ceu_data; + struct ceu_device *ceudev; + struct resource *res; + unsigned int irq; + int num_subdevs; + int ret; + + ceudev = kzalloc(sizeof(*ceudev), GFP_KERNEL); + if (!ceudev) + return -ENOMEM; + + platform_set_drvdata(pdev, ceudev); + ceudev->dev = dev; + + INIT_LIST_HEAD(&ceudev->capture); + spin_lock_init(&ceudev->lock); + mutex_init(&ceudev->mlock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ceudev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ceudev->base)) { + ret = PTR_ERR(ceudev->base); + goto error_free_ceudev; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "Failed to get irq: %d\n", ret); + goto error_free_ceudev; + } + irq = ret; + + ret = devm_request_irq(dev, irq, ceu_irq, + 0, dev_name(dev), ceudev); + if (ret) { + dev_err(&pdev->dev, "Unable to request CEU interrupt.\n"); + goto error_free_ceudev; + } + + pm_runtime_enable(dev); + + ret = v4l2_device_register(dev, &ceudev->v4l2_dev); + if (ret) + goto error_pm_disable; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + ceu_data = of_match_device(ceu_of_match, dev)->data; + num_subdevs = ceu_parse_dt(ceudev); + } else if (dev->platform_data) { + /* Assume SH4 if booting with platform data. */ + ceu_data = &ceu_data_sh4; + num_subdevs = ceu_parse_platform_data(ceudev, + dev->platform_data); + } else { + num_subdevs = -EINVAL; + } + + if (num_subdevs < 0) { + ret = num_subdevs; + goto error_v4l2_unregister; + } + ceudev->irq_mask = ceu_data->irq_mask; + + ceudev->notifier.v4l2_dev = &ceudev->v4l2_dev; + ceudev->notifier.subdevs = ceudev->asds; + ceudev->notifier.num_subdevs = num_subdevs; + ceudev->notifier.ops = &ceu_notify_ops; + ret = v4l2_async_notifier_register(&ceudev->v4l2_dev, + &ceudev->notifier); + if (ret) + goto error_v4l2_unregister; + + dev_info(dev, "Renesas Capture Engine Unit %s\n", dev_name(dev)); + + return 0; + +error_v4l2_unregister: + v4l2_device_unregister(&ceudev->v4l2_dev); +error_pm_disable: + pm_runtime_disable(dev); +error_free_ceudev: + kfree(ceudev); + + return ret; +} + +static int ceu_remove(struct platform_device *pdev) +{ + struct ceu_device *ceudev = platform_get_drvdata(pdev); + + pm_runtime_disable(ceudev->dev); + + v4l2_async_notifier_unregister(&ceudev->notifier); + + v4l2_device_unregister(&ceudev->v4l2_dev); + + video_unregister_device(&ceudev->vdev); + + return 0; +} + +static const struct dev_pm_ops ceu_pm_ops = { + SET_RUNTIME_PM_OPS(ceu_runtime_suspend, + ceu_runtime_resume, + NULL) +}; + +static struct platform_driver ceu_driver = { + .driver = { + .name = DRIVER_NAME, + .pm = &ceu_pm_ops, + .of_match_table = of_match_ptr(ceu_of_match), + }, + .probe = ceu_probe, + .remove = ceu_remove, +}; + +module_platform_driver(ceu_driver); + +MODULE_DESCRIPTION("Renesas CEU camera driver"); +MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 49cacc7a48d1..fa1ba98c96dc 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -140,7 +140,8 @@ void rga_buf_map(struct vb2_buffer *vb) address = sg_phys(sgl); for (p = 0; p < len; p++) { - dma_addr_t phys = address + (p << PAGE_SHIFT); + dma_addr_t phys = address + + ((dma_addr_t)p << PAGE_SHIFT); pages[mapped_size + p] = phys; } diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 89296de9cf4a..d508a8ba6f89 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -207,7 +207,7 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } -struct rga_fmt formats[] = { +static struct rga_fmt formats[] = { { .fourcc = V4L2_PIX_FMT_ARGB32, .color_swap = RGA_COLOR_RB_SWAP, diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 437395a61065..9ab8e7ee2e1e 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1256,16 +1256,17 @@ static void __camif_subdev_try_format(struct camif_dev *camif, { const struct s3c_camif_variant *variant = camif->variant; const struct vp_pix_limits *pix_lim; - int i = ARRAY_SIZE(camif_mbus_formats); + unsigned int i; /* FIXME: constraints against codec or preview path ? */ pix_lim = &variant->vp_pix_limits[VP_CODEC]; - while (i-- >= 0) + for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++) if (camif_mbus_formats[i] == mf->code) break; - mf->code = camif_mbus_formats[i]; + if (i == ARRAY_SIZE(camif_mbus_formats)) + mf->code = camif_mbus_formats[0]; if (pad == CAMIF_SD_PAD_SINK) { v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h b/drivers/media/platform/s5p-mfc/regs-mfc-v10.h new file mode 100644 index 000000000000..fadd9139b489 --- /dev/null +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v10.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Register definition file for Samsung MFC V10.x Interface (FIMV) driver + * + */ + +#ifndef _REGS_MFC_V10_H +#define _REGS_MFC_V10_H + +#include <linux/sizes.h> +#include "regs-mfc-v8.h" + +/* MFCv10 register definitions*/ +#define S5P_FIMV_MFC_CLOCK_OFF_V10 0x7120 +#define S5P_FIMV_MFC_STATE_V10 0x7124 +#define S5P_FIMV_D_STATIC_BUFFER_ADDR_V10 0xF570 +#define S5P_FIMV_D_STATIC_BUFFER_SIZE_V10 0xF574 +#define S5P_FIMV_E_NUM_T_LAYER_V10 0xFBAC +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER0_V10 0xFBB0 +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER1_V10 0xFBB4 +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER2_V10 0xFBB8 +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER3_V10 0xFBBC +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER4_V10 0xFBC0 +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER5_V10 0xFBC4 +#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER6_V10 0xFBC8 +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0_V10 0xFD18 +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER1_V10 0xFD1C +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER2_V10 0xFD20 +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER3_V10 0xFD24 +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER4_V10 0xFD28 +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER5_V10 0xFD2C +#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER6_V10 0xFD30 +#define S5P_FIMV_E_HEVC_OPTIONS_V10 0xFDD4 +#define S5P_FIMV_E_HEVC_REFRESH_PERIOD_V10 0xFDD8 +#define S5P_FIMV_E_HEVC_CHROMA_QP_OFFSET_V10 0xFDDC +#define S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2_V10 0xFDE0 +#define S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2_V10 0xFDE4 +#define S5P_FIMV_E_HEVC_NAL_CONTROL_V10 0xFDE8 + +/* MFCv10 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V10 (30 * SZ_1K) +#define MFC_H264_DEC_CTX_BUF_SIZE_V10 (2 * SZ_1M) +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V10 (20 * SZ_1K) +#define MFC_H264_ENC_CTX_BUF_SIZE_V10 (100 * SZ_1K) +#define MFC_HEVC_ENC_CTX_BUF_SIZE_V10 (30 * SZ_1K) +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V10 (15 * SZ_1K) + +/* MFCv10 variant defines */ +#define MAX_FW_SIZE_V10 (SZ_1M) +#define MAX_CPB_SIZE_V10 (3 * SZ_1M) +#define MFC_VERSION_V10 0xA0 +#define MFC_NUM_PORTS_V10 1 + +/* MFCv10 codec defines*/ +#define S5P_FIMV_CODEC_HEVC_DEC 17 +#define S5P_FIMV_CODEC_VP9_DEC 18 +#define S5P_FIMV_CODEC_HEVC_ENC 26 + +/* Decoder buffer size for MFC v10 */ +#define DEC_VP9_STATIC_BUFFER_SIZE 20480 + +/* Encoder buffer size for MFC v10.0 */ +#define ENC_V100_BASE_SIZE(x, y) \ + (((x + 3) * (y + 3) * 8) \ + + ((y * 64) + 1280) * DIV_ROUND_UP(x, 8)) + +#define ENC_V100_H264_ME_SIZE(x, y) \ + (ENC_V100_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP(x * y, 64) * 32)) + +#define ENC_V100_MPEG4_ME_SIZE(x, y) \ + (ENC_V100_BASE_SIZE(x, y) \ + + (DIV_ROUND_UP(x * y, 128) * 16)) + +#define ENC_V100_VP8_ME_SIZE(x, y) \ + ENC_V100_BASE_SIZE(x, y) + +#define ENC_V100_HEVC_ME_SIZE(x, y) \ + (((x + 3) * (y + 3) * 32) \ + + ((y * 128) + 1280) * DIV_ROUND_UP(x, 4)) + +#endif /*_REGS_MFC_V10_H*/ + diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h index 75f5f7511d72..bd639ae71023 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h @@ -17,6 +17,7 @@ /* Additional registers for v8 */ #define S5P_FIMV_D_MVC_NUM_VIEWS_V8 0xf104 +#define S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8 0xf108 #define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144 #define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148 #define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150 @@ -84,6 +85,7 @@ #define S5P_FIMV_E_VBV_BUFFER_SIZE_V8 0xf78c #define S5P_FIMV_E_VBV_INIT_DELAY_V8 0xf790 +#define S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8 0xf894 #define S5P_FIMV_E_ASPECT_RATIO_V8 0xfb4c #define S5P_FIMV_E_EXTENDED_SAR_V8 0xfb50 diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index d5b94fc0040e..a80251ed3143 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -526,6 +526,8 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, dev); ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count, dev); + ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, + get_min_scratch_buf_size, dev); if (ctx->img_width == 0 || ctx->img_height == 0) ctx->state = MFCINST_ERROR; else @@ -1607,6 +1609,29 @@ static struct s5p_mfc_variant mfc_drvdata_v8_5433 = { .num_clocks = 3, }; +static struct s5p_mfc_buf_size_v6 mfc_buf_size_v10 = { + .dev_ctx = MFC_CTX_BUF_SIZE_V10, + .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V10, + .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V10, + .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V10, + .hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V10, + .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V10, +}; + +static struct s5p_mfc_buf_size buf_size_v10 = { + .fw = MAX_FW_SIZE_V10, + .cpb = MAX_CPB_SIZE_V10, + .priv = &mfc_buf_size_v10, +}; + +static struct s5p_mfc_variant mfc_drvdata_v10 = { + .version = MFC_VERSION_V10, + .version_bit = MFC_V10_BIT, + .port_num = MFC_NUM_PORTS_V10, + .buf_size = &buf_size_v10, + .fw_name[0] = "s5p-mfc-v10.fw", +}; + static const struct of_device_id exynos_mfc_match[] = { { .compatible = "samsung,mfc-v5", @@ -1623,6 +1648,9 @@ static const struct of_device_id exynos_mfc_match[] = { }, { .compatible = "samsung,exynos5433-mfc", .data = &mfc_drvdata_v8_5433, + }, { + .compatible = "samsung,mfc-v10", + .data = &mfc_drvdata_v10, }, {}, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index b1b149151d2d..7521fceb68f1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -101,6 +101,12 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) case S5P_MFC_CODEC_VP8_DEC: codec_type = S5P_FIMV_CODEC_VP8_DEC_V6; break; + case S5P_MFC_CODEC_HEVC_DEC: + codec_type = S5P_FIMV_CODEC_HEVC_DEC; + break; + case S5P_MFC_CODEC_VP9_DEC: + codec_type = S5P_FIMV_CODEC_VP9_DEC; + break; case S5P_MFC_CODEC_H264_ENC: codec_type = S5P_FIMV_CODEC_H264_ENC_V6; break; @@ -116,6 +122,9 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) case S5P_MFC_CODEC_VP8_ENC: codec_type = S5P_FIMV_CODEC_VP8_ENC_V7; break; + case S5P_MFC_CODEC_HEVC_ENC: + codec_type = S5P_FIMV_CODEC_HEVC_ENC; + break; default: codec_type = S5P_FIMV_CODEC_NONE_V6; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 76119a8cc477..20442a9b9f7a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -23,7 +23,7 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-v4l2.h> #include "regs-mfc.h" -#include "regs-mfc-v8.h" +#include "regs-mfc-v10.h" #define S5P_MFC_NAME "s5p-mfc" @@ -61,7 +61,7 @@ #define MFC_ENC_CAP_PLANE_COUNT 1 #define MFC_ENC_OUT_PLANE_COUNT 2 #define STUFF_BYTE 4 -#define MFC_MAX_CTRLS 77 +#define MFC_MAX_CTRLS 128 #define S5P_MFC_CODEC_NONE -1 #define S5P_MFC_CODEC_H264_DEC 0 @@ -72,12 +72,15 @@ #define S5P_MFC_CODEC_H263_DEC 5 #define S5P_MFC_CODEC_VC1RCV_DEC 6 #define S5P_MFC_CODEC_VP8_DEC 7 +#define S5P_MFC_CODEC_HEVC_DEC 17 +#define S5P_MFC_CODEC_VP9_DEC 18 #define S5P_MFC_CODEC_H264_ENC 20 #define S5P_MFC_CODEC_H264_MVC_ENC 21 #define S5P_MFC_CODEC_MPEG4_ENC 22 #define S5P_MFC_CODEC_H263_ENC 23 #define S5P_MFC_CODEC_VP8_ENC 24 +#define S5P_MFC_CODEC_HEVC_ENC 26 #define S5P_MFC_R2H_CMD_EMPTY 0 #define S5P_MFC_R2H_CMD_SYS_INIT_RET 1 @@ -213,6 +216,7 @@ struct s5p_mfc_buf_size_v6 { unsigned int h264_dec_ctx; unsigned int other_dec_ctx; unsigned int h264_enc_ctx; + unsigned int hevc_enc_ctx; unsigned int other_enc_ctx; }; @@ -430,6 +434,55 @@ struct s5p_mfc_vp8_enc_params { u8 profile; }; +struct s5p_mfc_hevc_enc_params { + enum v4l2_mpeg_video_hevc_profile profile; + int level; + enum v4l2_mpeg_video_h264_level level_v4l2; + u8 tier; + u32 rc_framerate; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_lcu_dark; + u8 rc_lcu_smooth; + u8 rc_lcu_static; + u8 rc_lcu_activity; + u8 rc_frame_qp; + u8 rc_p_frame_qp; + u8 rc_b_frame_qp; + u8 max_partition_depth; + u8 num_refs_for_p; + u8 refreshtype; + u16 refreshperiod; + s32 lf_beta_offset_div2; + s32 lf_tc_offset_div2; + u8 loopfilter; + u8 loopfilter_disable; + u8 loopfilter_across; + u8 nal_control_length_filed; + u8 nal_control_user_ref; + u8 nal_control_store_ref; + u8 const_intra_period_enable; + u8 lossless_cu_enable; + u8 wavefront_enable; + u8 enable_ltr; + u8 hier_qp_enable; + enum v4l2_mpeg_video_hevc_hier_coding_type hier_qp_type; + u8 num_hier_layer; + u8 hier_qp_layer[7]; + u32 hier_bit_layer[7]; + u8 sign_data_hiding; + u8 general_pb_enable; + u8 temporal_id_enable; + u8 strong_intra_smooth; + u8 intra_pu_split_disable; + u8 tmv_prediction_disable; + u8 max_num_merge_mv; + u8 eco_mode_enable; + u8 encoding_nostartcode_enable; + u8 size_of_length_field; + u8 prepend_sps_pps_to_idr; +}; + /** * struct s5p_mfc_enc_params - general encoding parameters */ @@ -467,6 +520,7 @@ struct s5p_mfc_enc_params { struct s5p_mfc_h264_enc_params h264; struct s5p_mfc_mpeg4_enc_params mpeg4; struct s5p_mfc_vp8_enc_params vp8; + struct s5p_mfc_hevc_enc_params hevc; } codec; }; @@ -714,12 +768,20 @@ void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq); #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) #define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) #define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0) -#define IS_MFCV8(dev) (dev->variant->version >= 0x80 ? 1 : 0) +#define IS_MFCV8_PLUS(dev) (dev->variant->version >= 0x80 ? 1 : 0) +#define IS_MFCV10(dev) (dev->variant->version >= 0xA0 ? 1 : 0) +#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10(dev)) #define MFC_V5_BIT BIT(0) #define MFC_V6_BIT BIT(1) #define MFC_V7_BIT BIT(2) #define MFC_V8_BIT BIT(3) +#define MFC_V10_BIT BIT(5) +#define MFC_V5PLUS_BITS (MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | \ + MFC_V8_BIT | MFC_V10_BIT) +#define MFC_V6PLUS_BITS (MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT | \ + MFC_V10_BIT) +#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT) #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index f95cd76af537..ee7b15b335e0 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -62,7 +62,7 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) if (!dev->variant->fw_name[i]) continue; err = request_firmware((const struct firmware **)&fw_blob, - dev->variant->fw_name[i], dev->v4l2_dev.dev); + dev->variant->fw_name[i], &dev->plat_dev->dev); if (!err) { dev->fw_ver = (enum s5p_mfc_fw_ver) i; break; @@ -239,6 +239,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) } else mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); + + if (IS_MFCV10(dev)) + mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10); + mfc_debug(2, "Will now wait for completion of firmware transfer\n"); if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) { mfc_err("Failed to load firmware\n"); @@ -399,7 +403,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev) s5p_mfc_clear_cmds(dev); s5p_mfc_clean_dev_int_flags(dev); /* 3. Send MFC wakeup command and wait for completion*/ - if (IS_MFCV8(dev)) + if (IS_MFCV8_PLUS(dev)) ret = s5p_mfc_v8_wait_wakeup(dev); else ret = s5p_mfc_wait_wakeup(dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8937b0af7cb3..5cf4d9921264 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -54,7 +54,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, - .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V6PLUS_BITS, }, { .name = "4:2:0 2 Planes Y/CrCb", @@ -62,7 +62,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, - .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V6PLUS_BITS, }, { .name = "H264 Encoded Stream", @@ -70,8 +70,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "H264/MVC Encoded Stream", @@ -79,7 +78,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_MVC_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V6PLUS_BITS, }, { .name = "H263 Encoded Stream", @@ -87,8 +86,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H263_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "MPEG1 Encoded Stream", @@ -96,8 +94,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG2_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "MPEG2 Encoded Stream", @@ -105,8 +102,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG2_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "MPEG4 Encoded Stream", @@ -114,8 +110,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "XviD Encoded Stream", @@ -123,8 +118,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "VC1 Encoded Stream", @@ -132,8 +126,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VC1_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "VC1 RCV Encoded Stream", @@ -141,8 +134,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VC1RCV_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "VP8 Encoded Stream", @@ -150,7 +142,21 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VP8_DEC, .type = MFC_FMT_DEC, .num_planes = 1, - .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V6PLUS_BITS, + }, + { + .fourcc = V4L2_PIX_FMT_HEVC, + .codec_mode = S5P_FIMV_CODEC_HEVC_DEC, + .type = MFC_FMT_DEC, + .num_planes = 1, + .versions = MFC_V10_BIT, + }, + { + .fourcc = V4L2_PIX_FMT_VP9, + .codec_mode = S5P_FIMV_CODEC_VP9_DEC, + .type = MFC_FMT_DEC, + .num_planes = 1, + .versions = MFC_V10_BIT, }, }; @@ -1177,7 +1183,7 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx) struct v4l2_format f; f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; ctx->src_fmt = find_format(&f, MFC_FMT_DEC); - if (IS_MFCV8(ctx->dev)) + if (IS_MFCV8_PLUS(ctx->dev)) f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; else if (IS_MFCV6_PLUS(ctx->dev)) f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 0d5d465561be..5c0462ca9993 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -57,8 +57,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "4:2:0 2 Planes Y/CrCb", @@ -66,7 +65,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, - .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V6PLUS_BITS, }, { .name = "H264 Encoded Stream", @@ -74,8 +73,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "MPEG4 Encoded Stream", @@ -83,8 +81,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "H263 Encoded Stream", @@ -92,8 +89,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H263_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | - MFC_V8_BIT, + .versions = MFC_V5PLUS_BITS, }, { .name = "VP8 Encoded Stream", @@ -101,7 +97,14 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VP8_ENC, .type = MFC_FMT_ENC, .num_planes = 1, - .versions = MFC_V7_BIT | MFC_V8_BIT, + .versions = MFC_V7PLUS_BITS, + }, + { + .fourcc = V4L2_PIX_FMT_HEVC, + .codec_mode = S5P_FIMV_CODEC_HEVC_ENC, + .type = MFC_FMT_ENC, + .num_planes = 1, + .versions = MFC_V10_BIT, }, }; @@ -697,6 +700,368 @@ static struct mfc_control controls[] = { .default_value = 0, }, { + .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HEVC I Frame QP Value", + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HEVC P Frame QP Value", + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .maximum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .maximum = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_TIER, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, + .maximum = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = (1 << 16) - 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 2, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE, + .maximum = V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED, + .maximum = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .maximum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = INT_MIN, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (1 << 16) - 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEVC_SIZE_0, + .maximum = V4L2_MPEG_VIDEO_HEVC_SIZE_4, + .step = 1, + .default_value = V4L2_MPEG_VIDEO_HEVC_SIZE_0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { .id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Minimum number of output bufs", @@ -817,6 +1182,11 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) get_enc_dpb_count, dev); if (ctx->pb_count < enc_pb_count) ctx->pb_count = enc_pb_count; + if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) { + ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, + get_e_min_scratch_buf_size, dev); + ctx->bank1.size += ctx->scratch_buf_size; + } ctx->state = MFCINST_HEAD_PRODUCED; } @@ -850,7 +1220,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *mb_entry; - unsigned long enc_y_addr, enc_c_addr; + unsigned long enc_y_addr = 0, enc_c_addr = 0; unsigned long mb_y_addr, mb_c_addr; int slice_type; unsigned int strm_size; @@ -1358,6 +1728,26 @@ static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl) return t[lvl]; } +static inline int hevc_level(enum v4l2_mpeg_video_hevc_level lvl) +{ + static unsigned int t[] = { + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_1 */ 10, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_2 */ 20, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 */ 21, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_3 */ 30, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 */ 31, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_4 */ 40, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 */ 41, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5 */ 50, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 */ 51, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 */ 52, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6 */ 60, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 */ 61, + /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 */ 62, + }; + return t[lvl]; +} + static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar) { static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = { @@ -1383,6 +1773,42 @@ static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar) return t[sar]; } +/* + * Update range of all HEVC quantization parameter controls that depend on the + * V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP controls. + */ +static void __enc_update_hevc_qp_ctrls_range(struct s5p_mfc_ctx *ctx, + int min, int max) +{ + static const int __hevc_qp_ctrls[] = { + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP, + V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP, + }; + struct v4l2_ctrl *ctrl = NULL; + int i, j; + + for (i = 0; i < ARRAY_SIZE(__hevc_qp_ctrls); i++) { + for (j = 0; j < ARRAY_SIZE(ctx->ctrls); j++) { + if (ctx->ctrls[j]->id == __hevc_qp_ctrls[i]) { + ctrl = ctx->ctrls[j]; + break; + } + } + if (WARN_ON(!ctrl)) + break; + + __v4l2_ctrl_modify_range(ctrl, min, max, ctrl->step, min); + } +} + static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) { struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl); @@ -1634,6 +2060,157 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: p->codec.vp8.profile = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + p->codec.hevc.rc_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: + p->codec.hevc.rc_p_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: + p->codec.hevc.rc_b_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION: + p->codec.hevc.rc_framerate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + p->codec.hevc.rc_min_qp = ctrl->val; + __enc_update_hevc_qp_ctrls_range(ctx, ctrl->val, + p->codec.hevc.rc_max_qp); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + p->codec.hevc.rc_max_qp = ctrl->val; + __enc_update_hevc_qp_ctrls_range(ctx, p->codec.hevc.rc_min_qp, + ctrl->val); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + p->codec.hevc.level_v4l2 = ctrl->val; + p->codec.hevc.level = hevc_level(ctrl->val); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + p->codec.hevc.profile = + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; + break; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + p->codec.hevc.profile = + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE; + break; + default: + ret = -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + p->codec.hevc.tier = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH: + p->codec.hevc.max_partition_depth = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: + p->codec.hevc.num_refs_for_p = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + p->codec.hevc.refreshtype = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: + p->codec.hevc.const_intra_period_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: + p->codec.hevc.lossless_cu_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: + p->codec.hevc.wavefront_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + p->codec.hevc.loopfilter = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP: + p->codec.hevc.hier_qp_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + p->codec.hevc.hier_qp_type = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: + p->codec.hevc.num_hier_layer = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP: + p->codec.hevc.hier_qp_layer[0] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP: + p->codec.hevc.hier_qp_layer[1] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP: + p->codec.hevc.hier_qp_layer[2] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP: + p->codec.hevc.hier_qp_layer[3] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP: + p->codec.hevc.hier_qp_layer[4] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP: + p->codec.hevc.hier_qp_layer[5] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP: + p->codec.hevc.hier_qp_layer[6] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: + p->codec.hevc.hier_bit_layer[0] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: + p->codec.hevc.hier_bit_layer[1] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: + p->codec.hevc.hier_bit_layer[2] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: + p->codec.hevc.hier_bit_layer[3] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: + p->codec.hevc.hier_bit_layer[4] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: + p->codec.hevc.hier_bit_layer[5] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR: + p->codec.hevc.hier_bit_layer[6] = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB: + p->codec.hevc.general_pb_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID: + p->codec.hevc.temporal_id_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: + p->codec.hevc.strong_intra_smooth = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT: + p->codec.hevc.intra_pu_split_disable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: + p->codec.hevc.tmv_prediction_disable = !ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: + p->codec.hevc.max_num_merge_mv = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE: + p->codec.hevc.encoding_nostartcode_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: + p->codec.hevc.refreshperiod = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: + p->codec.hevc.lf_beta_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: + p->codec.hevc.lf_tc_offset_div2 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + p->codec.hevc.size_of_length_field = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: + p->codec.hevc.prepend_sps_pps_to_idr = ctrl->val; + break; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 16d553fcff08..8c295f0f9740 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -169,6 +169,9 @@ struct s5p_mfc_regs { void __iomem *d_decoded_third_addr;/* only v7 */ void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */ void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */ + void __iomem *d_min_scratch_buffer_size; /* v10 */ + void __iomem *d_static_buffer_addr; /* v10 */ + void __iomem *d_static_buffer_size; /* v10 */ /* encoder registers */ void __iomem *e_frame_width; @@ -268,6 +271,15 @@ struct s5p_mfc_regs { void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ + void __iomem *e_min_scratch_buffer_size; /* v10 */ + void __iomem *e_num_t_layer; /* v10 */ + void __iomem *e_hier_qp_layer0; /* v10 */ + void __iomem *e_hier_bit_rate_layer0; /* v10 */ + void __iomem *e_hevc_options; /* v10 */ + void __iomem *e_hevc_refresh_period; /* v10 */ + void __iomem *e_hevc_lf_beta_offset_div2; /* v10 */ + void __iomem *e_hevc_lf_tc_offset_div2; /* v10 */ + void __iomem *e_hevc_nal_control; /* v10 */ }; struct s5p_mfc_hw_ops { @@ -311,6 +323,8 @@ struct s5p_mfc_hw_ops { unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_v)(struct s5p_mfc_ctx *ctx); + int (*get_min_scratch_buf_size)(struct s5p_mfc_dev *dev); + int (*get_e_min_scratch_buf_size)(struct s5p_mfc_dev *dev); }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 88dbb9c341ec..7c629be43205 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -64,6 +64,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; + unsigned int lcu_width = 0, lcu_height = 0; int ret; mb_width = MB_WIDTH(ctx->img_width); @@ -74,7 +75,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->luma_size, ctx->chroma_size, ctx->mv_size); mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count); } else if (ctx->type == MFCINST_ENCODER) { - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) { + ctx->tmv_buffer_size = 0; + } else if (IS_MFCV8_PLUS(dev)) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); @@ -82,14 +85,37 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); - - ctx->luma_dpb_size = ALIGN((mb_width * mb_height) * - S5P_FIMV_LUMA_MB_TO_PIXEL_V6, - S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6); - ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) * - S5P_FIMV_CHROMA_MB_TO_PIXEL_V6, - S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6); - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) { + lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width); + lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height); + if (ctx->codec_mode != S5P_FIMV_CODEC_HEVC_ENC) { + ctx->luma_dpb_size = + ALIGN((mb_width * 16), 64) + * ALIGN((mb_height * 16), 32) + + 64; + ctx->chroma_dpb_size = + ALIGN((mb_width * 16), 64) + * (mb_height * 8) + + 64; + } else { + ctx->luma_dpb_size = + ALIGN((lcu_width * 32), 64) + * ALIGN((lcu_height * 32), 32) + + 64; + ctx->chroma_dpb_size = + ALIGN((lcu_width * 32), 64) + * (lcu_height * 16) + + 64; + } + } else { + ctx->luma_dpb_size = ALIGN((mb_width * mb_height) * + S5P_FIMV_LUMA_MB_TO_PIXEL_V6, + S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6); + ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) * + S5P_FIMV_CHROMA_MB_TO_PIXEL_V6, + S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6); + } + if (IS_MFCV8_PLUS(dev)) ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8( ctx->img_width, ctx->img_height, mb_width, mb_height), @@ -110,7 +136,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: case S5P_MFC_CODEC_H264_MVC_DEC: - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) + mfc_debug(2, "Use min scratch buffer size\n"); + else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8( mb_width, @@ -127,7 +155,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) (ctx->mv_count * ctx->mv_size); break; case S5P_MFC_CODEC_MPEG4_DEC: - if (IS_MFCV7_PLUS(dev)) { + if (IS_MFCV10(dev)) + mfc_debug(2, "Use min scratch buffer size\n"); + else if (IS_MFCV7_PLUS(dev)) { ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7( mb_width, @@ -145,10 +175,14 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6( - mb_width, - mb_height); + if (IS_MFCV10(dev)) + mfc_debug(2, "Use min scratch buffer size\n"); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6( + mb_width, + mb_height); + ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size; @@ -158,16 +192,21 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6( - mb_width, - mb_height); + if (IS_MFCV10(dev)) + mfc_debug(2, "Use min scratch buffer size\n"); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6( + mb_width, + mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) + mfc_debug(2, "Use min scratch buffer size\n"); + else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8( mb_width, @@ -181,8 +220,24 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size; break; + case S5P_MFC_CODEC_HEVC_DEC: + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->bank1.size = + ctx->scratch_buf_size + + (ctx->mv_count * ctx->mv_size); + break; + case S5P_MFC_CODEC_VP9_DEC: + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->bank1.size = + ctx->scratch_buf_size + + DEC_VP9_STATIC_BUFFER_SIZE; + break; case S5P_MFC_CODEC_H264_ENC: - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), 16); + } else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8( mb_width, @@ -202,10 +257,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_ENC_V6( - mb_width, - mb_height); + if (IS_MFCV10(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, + mb_height), 16); + } else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_ENC_V6( + mb_width, + mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = @@ -215,7 +276,12 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VP8_ENC: - if (IS_MFCV8(dev)) + if (IS_MFCV10(dev)) { + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), + 16); + } else if (IS_MFCV8_PLUS(dev)) ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8( mb_width, @@ -233,6 +299,17 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->chroma_dpb_size + ctx->me_buffer_size)); ctx->bank2.size = 0; break; + case S5P_MFC_CODEC_HEVC_ENC: + mfc_debug(2, "Use min scratch buffer size\n"); + ctx->me_buffer_size = + ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16); + ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256); + ctx->bank1.size = + ctx->scratch_buf_size + ctx->tmv_buffer_size + + (ctx->pb_count * (ctx->luma_dpb_size + + ctx->chroma_dpb_size + ctx->me_buffer_size)); + ctx->bank2.size = 0; + break; default: break; } @@ -267,6 +344,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: case S5P_MFC_CODEC_H264_MVC_DEC: + case S5P_MFC_CODEC_HEVC_DEC: ctx->ctx.size = buf_size->h264_dec_ctx; break; case S5P_MFC_CODEC_MPEG4_DEC: @@ -275,11 +353,15 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) case S5P_MFC_CODEC_VC1_DEC: case S5P_MFC_CODEC_MPEG2_DEC: case S5P_MFC_CODEC_VP8_DEC: + case S5P_MFC_CODEC_VP9_DEC: ctx->ctx.size = buf_size->other_dec_ctx; break; case S5P_MFC_CODEC_H264_ENC: ctx->ctx.size = buf_size->h264_enc_ctx; break; + case S5P_MFC_CODEC_HEVC_ENC: + ctx->ctx.size = buf_size->hevc_enc_ctx; + break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: case S5P_MFC_CODEC_VP8_ENC: @@ -356,6 +438,7 @@ static int calc_plane(int width, int height) static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) { + struct s5p_mfc_dev *dev = ctx->dev; ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6); mfc_debug(2, "SEQ Done: Movie dimensions %dx%d,\n" @@ -364,7 +447,7 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height); ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1)); - if (IS_MFCV8(ctx->dev)) { + if (IS_MFCV8_PLUS(ctx->dev)) { /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/ ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; @@ -372,9 +455,17 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { - ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, + if (IS_MFCV10(dev)) { + ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V10(ctx->img_width, + ctx->img_height); + } else { + ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, + ctx->img_height); + } + } else if (ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) { + ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width, ctx->img_height); - ctx->mv_size = ALIGN(ctx->mv_size, 16); + ctx->mv_size = ALIGN(ctx->mv_size, 32); } else { ctx->mv_size = 0; } @@ -445,7 +536,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) writel(buf_addr1, mfc_regs->d_scratch_buffer_addr); writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); - if (IS_MFCV8(dev)) { + if (IS_MFCV8_PLUS(dev)) { writel(ctx->img_width, mfc_regs->d_first_plane_dpb_stride_size); writel(ctx->img_width, @@ -456,7 +547,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) buf_size1 -= ctx->scratch_buf_size; if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC || - ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){ + ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC || + ctx->codec_mode == S5P_FIMV_CODEC_HEVC_DEC) { writel(ctx->mv_size, mfc_regs->d_mv_buffer_size); writel(ctx->mv_count, mfc_regs->d_num_mv); } @@ -479,7 +571,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_regs->d_second_plane_dpb + i * 4); } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || - ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { + ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC || + ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) { for (i = 0; i < ctx->mv_count; i++) { /* To test alignment */ align_gap = buf_addr1; @@ -494,6 +587,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) buf_size1 -= frame_size_mv; } } + if (ctx->codec_mode == S5P_FIMV_CODEC_VP9_DEC) { + writel(buf_addr1, mfc_regs->d_static_buffer_addr); + writel(DEC_VP9_STATIC_BUFFER_SIZE, + mfc_regs->d_static_buffer_size); + buf_addr1 += DEC_VP9_STATIC_BUFFER_SIZE; + buf_size1 -= DEC_VP9_STATIC_BUFFER_SIZE; + } mfc_debug(2, "Buf1: %zx, buf_size1: %d (frames %d)\n", buf_addr1, buf_size1, ctx->total_dpb_count); @@ -571,15 +671,34 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); - for (i = 0; i < ctx->pb_count; i++) { - writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); - buf_addr1 += ctx->luma_dpb_size; - writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); - buf_addr1 += ctx->chroma_dpb_size; - writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); - buf_addr1 += ctx->me_buffer_size; - buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size + - ctx->me_buffer_size); + if (IS_MFCV10(dev)) { + /* start address of per buffer is aligned */ + for (i = 0; i < ctx->pb_count; i++) { + writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); + buf_addr1 += ctx->luma_dpb_size; + buf_size1 -= ctx->luma_dpb_size; + } + for (i = 0; i < ctx->pb_count; i++) { + writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); + buf_addr1 += ctx->chroma_dpb_size; + buf_size1 -= ctx->chroma_dpb_size; + } + for (i = 0; i < ctx->pb_count; i++) { + writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); + buf_addr1 += ctx->me_buffer_size; + buf_size1 -= ctx->me_buffer_size; + } + } else { + for (i = 0; i < ctx->pb_count; i++) { + writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); + buf_addr1 += ctx->luma_dpb_size; + writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); + buf_addr1 += ctx->chroma_dpb_size; + writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); + buf_addr1 += ctx->me_buffer_size; + buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size + + ctx->me_buffer_size); + } } writel(buf_addr1, mfc_regs->e_scratch_buffer_addr); @@ -1321,6 +1440,162 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) return 0; } +static int s5p_mfc_set_enc_params_hevc(struct s5p_mfc_ctx *ctx) +{ + struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; + struct s5p_mfc_enc_params *p = &ctx->enc_params; + struct s5p_mfc_hevc_enc_params *p_hevc = &p->codec.hevc; + unsigned int reg = 0; + int i; + + mfc_debug_enter(); + + s5p_mfc_set_enc_params(ctx); + + /* pictype : number of B */ + reg = readl(mfc_regs->e_gop_config); + /* num_b_frame - 0 ~ 2 */ + reg &= ~(0x3 << 16); + reg |= (p->num_b_frame << 16); + writel(reg, mfc_regs->e_gop_config); + + /* UHD encoding case */ + if ((ctx->img_width == 3840) && (ctx->img_height == 2160)) { + p_hevc->level = 51; + p_hevc->tier = 0; + /* this tier can be changed */ + } + + /* tier & level */ + reg = 0; + /* profile */ + reg |= p_hevc->profile & 0x3; + /* level */ + reg &= ~(0xFF << 8); + reg |= (p_hevc->level << 8); + /* tier - 0 ~ 1 */ + reg |= (p_hevc->tier << 16); + writel(reg, mfc_regs->e_picture_profile); + + switch (p_hevc->loopfilter) { + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED: + p_hevc->loopfilter_disable = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED: + p_hevc->loopfilter_disable = 0; + p_hevc->loopfilter_across = 1; + break; + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: + p_hevc->loopfilter_disable = 0; + p_hevc->loopfilter_across = 0; + break; + } + + /* max partition depth */ + reg = 0; + reg |= (p_hevc->max_partition_depth & 0x1); + reg |= (p_hevc->num_refs_for_p-1) << 2; + reg |= (p_hevc->refreshtype & 0x3) << 3; + reg |= (p_hevc->const_intra_period_enable & 0x1) << 5; + reg |= (p_hevc->lossless_cu_enable & 0x1) << 6; + reg |= (p_hevc->wavefront_enable & 0x1) << 7; + reg |= (p_hevc->loopfilter_disable & 0x1) << 8; + reg |= (p_hevc->loopfilter_across & 0x1) << 9; + reg |= (p_hevc->enable_ltr & 0x1) << 10; + reg |= (p_hevc->hier_qp_enable & 0x1) << 11; + reg |= (p_hevc->general_pb_enable & 0x1) << 13; + reg |= (p_hevc->temporal_id_enable & 0x1) << 14; + reg |= (p_hevc->strong_intra_smooth & 0x1) << 15; + reg |= (p_hevc->intra_pu_split_disable & 0x1) << 16; + reg |= (p_hevc->tmv_prediction_disable & 0x1) << 17; + reg |= (p_hevc->max_num_merge_mv & 0x7) << 18; + reg |= (p_hevc->encoding_nostartcode_enable & 0x1) << 23; + reg |= (p_hevc->prepend_sps_pps_to_idr << 26); + + writel(reg, mfc_regs->e_hevc_options); + /* refresh period */ + if (p_hevc->refreshtype) { + reg = 0; + reg |= (p_hevc->refreshperiod & 0xFFFF); + writel(reg, mfc_regs->e_hevc_refresh_period); + } + /* loop filter setting */ + if (!(p_hevc->loopfilter_disable & 0x1)) { + reg = 0; + reg |= (p_hevc->lf_beta_offset_div2); + writel(reg, mfc_regs->e_hevc_lf_beta_offset_div2); + reg = 0; + reg |= (p_hevc->lf_tc_offset_div2); + writel(reg, mfc_regs->e_hevc_lf_tc_offset_div2); + } + /* hier qp enable */ + if (p_hevc->num_hier_layer) { + reg = 0; + reg |= (p_hevc->hier_qp_type & 0x1) << 0x3; + reg |= p_hevc->num_hier_layer & 0x7; + writel(reg, mfc_regs->e_num_t_layer); + /* QP value for each layer */ + if (p_hevc->hier_qp_enable) { + for (i = 0; i < 7; i++) + writel(p_hevc->hier_qp_layer[i], + mfc_regs->e_hier_qp_layer0 + i * 4); + } + if (p->rc_frame) { + for (i = 0; i < 7; i++) + writel(p_hevc->hier_bit_layer[i], + mfc_regs->e_hier_bit_rate_layer0 + + i * 4); + } + } + + /* rate control config. */ + reg = readl(mfc_regs->e_rc_config); + /* macroblock level rate control */ + reg &= ~(0x1 << 8); + reg |= (p->rc_mb << 8); + writel(reg, mfc_regs->e_rc_config); + /* frame QP */ + reg &= ~(0xFF); + reg |= p_hevc->rc_frame_qp; + writel(reg, mfc_regs->e_rc_config); + + /* frame rate */ + if (p->rc_frame) { + reg = 0; + reg &= ~(0xFFFF << 16); + reg |= ((p_hevc->rc_framerate) << 16); + reg &= ~(0xFFFF); + reg |= FRAME_DELTA_DEFAULT; + writel(reg, mfc_regs->e_rc_frame_rate); + } + + /* max & min value of QP */ + reg = 0; + /* max QP */ + reg &= ~(0xFF << 8); + reg |= (p_hevc->rc_max_qp << 8); + /* min QP */ + reg &= ~(0xFF); + reg |= p_hevc->rc_min_qp; + writel(reg, mfc_regs->e_rc_qp_bound); + + writel(0x0, mfc_regs->e_fixed_picture_qp); + if (!p->rc_frame && !p->rc_mb) { + reg = 0; + reg &= ~(0xFF << 16); + reg |= (p_hevc->rc_b_frame_qp << 16); + reg &= ~(0xFF << 8); + reg |= (p_hevc->rc_p_frame_qp << 8); + reg &= ~(0xFF); + reg |= p_hevc->rc_frame_qp; + writel(reg, mfc_regs->e_fixed_picture_qp); + } + mfc_debug_leave(); + + return 0; +} + /* Initialize decoding */ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) { @@ -1440,6 +1715,8 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params_h263(ctx); else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC) s5p_mfc_set_enc_params_vp8(ctx); + else if (ctx->codec_mode == S5P_FIMV_CODEC_HEVC_ENC) + s5p_mfc_set_enc_params_hevc(ctx); else { mfc_err("Unknown codec for encoding (%x).\n", ctx->codec_mode); @@ -1895,6 +2172,16 @@ static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) return readl(dev->mfc_regs->d_min_num_mv); } +static int s5p_mfc_get_min_scratch_buf_size(struct s5p_mfc_dev *dev) +{ + return readl(dev->mfc_regs->d_min_scratch_buffer_size); +} + +static int s5p_mfc_get_e_min_scratch_buf_size(struct s5p_mfc_dev *dev) +{ + return readl(dev->mfc_regs->e_min_scratch_buffer_size); +} + static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) { return readl(dev->mfc_regs->ret_instance_id); @@ -2109,7 +2396,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7); - if (!IS_MFCV8(dev)) + if (!IS_MFCV8_PLUS(dev)) goto done; /* Initialize registers used in MFC v8 only. @@ -2153,6 +2440,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8); R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8); R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8); + R(d_min_scratch_buffer_size, S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8); /* encoder registers */ R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8); @@ -2168,6 +2456,29 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8); R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8); R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8); + R(e_min_scratch_buffer_size, S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8); + + if (!IS_MFCV10(dev)) + goto done; + + /* Initialize registers used in MFC v10 only. + * Also, over-write the registers which have + * a different offset for MFC v10. + */ + + /* decoder registers */ + R(d_static_buffer_addr, S5P_FIMV_D_STATIC_BUFFER_ADDR_V10); + R(d_static_buffer_size, S5P_FIMV_D_STATIC_BUFFER_SIZE_V10); + + /* encoder registers */ + R(e_num_t_layer, S5P_FIMV_E_NUM_T_LAYER_V10); + R(e_hier_qp_layer0, S5P_FIMV_E_HIERARCHICAL_QP_LAYER0_V10); + R(e_hier_bit_rate_layer0, S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0_V10); + R(e_hevc_options, S5P_FIMV_E_HEVC_OPTIONS_V10); + R(e_hevc_refresh_period, S5P_FIMV_E_HEVC_REFRESH_PERIOD_V10); + R(e_hevc_lf_beta_offset_div2, S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2_V10); + R(e_hevc_lf_tc_offset_div2, S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2_V10); + R(e_hevc_nal_control, S5P_FIMV_E_HEVC_NAL_CONTROL_V10); done: return &mfc_regs; @@ -2216,6 +2527,8 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v6 = { .get_pic_type_bot = s5p_mfc_get_pic_type_bot_v6, .get_crop_info_h = s5p_mfc_get_crop_info_h_v6, .get_crop_info_v = s5p_mfc_get_crop_info_v_v6, + .get_min_scratch_buf_size = s5p_mfc_get_min_scratch_buf_size, + .get_e_min_scratch_buf_size = s5p_mfc_get_e_min_scratch_buf_size, }; struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h index 80558484bb40..f013b291ae5b 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h @@ -24,6 +24,13 @@ #define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, 16) #define S5P_MFC_DEC_MV_SIZE_V6(x, y) (MB_WIDTH(x) * \ (((MB_HEIGHT(y)+1)/2)*2) * 64 + 128) +#define S5P_MFC_DEC_MV_SIZE_V10(x, y) (MB_WIDTH(x) * \ + (((MB_HEIGHT(y)+1)/2)*2) * 64 + 512) +#define S5P_MFC_LCU_WIDTH(x_size) DIV_ROUND_UP(x_size, 32) +#define S5P_MFC_LCU_HEIGHT(y_size) DIV_ROUND_UP(y_size, 32) + +#define s5p_mfc_dec_hevc_mv_size(x, y) \ + (DIV_ROUND_UP(x, 64) * DIV_ROUND_UP(y, 64) * 256 + 512) /* Definition */ #define ENC_MULTI_SLICE_MB_MAX ((1 << 30) - 1) @@ -39,6 +46,14 @@ #define ENC_MPEG4_VOP_TIME_RES_MAX ((1 << 16) - 1) #define FRAME_DELTA_H264_H263 1 #define TIGHT_CBR_MAX 10 +#define ENC_HEVC_RC_FRAME_RATE_MAX ((1 << 16) - 1) +#define ENC_HEVC_QP_INDEX_MIN -12 +#define ENC_HEVC_QP_INDEX_MAX 12 +#define ENC_HEVC_LOOP_FILTER_MIN -12 +#define ENC_HEVC_LOOP_FILTER_MAX 12 +#define ENC_HEVC_LEVEL_MAX 62 + +#define FRAME_DELTA_DEFAULT 1 struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void); const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 976ea0bb5b6c..1a0cde017fdf 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -520,8 +520,8 @@ static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfm /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ unsigned int left = vfmt->frame.left & ~0x03; unsigned int top = vfmt->frame.top; - dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) + - top * veu->vfmt_out.bytesperline; + dma_addr_t offset = (dma_addr_t)top * veu->vfmt_out.bytesperline + + (((dma_addr_t)left * veu->vfmt_out.fmt->depth) >> 3); unsigned int y_line; vfmt->offset_y = offset; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index c86dd2fdab84..69f0d8e80bd8 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -787,7 +787,7 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); int err; - dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); if (icd->streamer != file) return -EBUSY; @@ -1788,17 +1788,19 @@ static int default_s_selection(struct soc_camera_device *icd, } static int default_g_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *parm) + struct v4l2_streamparm *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, g_parm, parm); + + return v4l2_g_parm_cap(icd->vdev, sd, a); } static int default_s_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *parm) + struct v4l2_streamparm *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, s_parm, parm); + + return v4l2_s_parm_cap(icd->vdev, sd, a); } static int default_enum_framesizes(struct soc_camera_device *icd, diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c index 0e5aa17bdd40..7c496bc1cf38 100644 --- a/drivers/media/platform/stm32/stm32-cec.c +++ b/drivers/media/platform/stm32/stm32-cec.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * STM32 CEC driver * Copyright (C) STMicroelectronics SA 2017 * - * 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. */ #include <linux/clk.h> diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 9460b3080dca..2e1933d872ee 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for STM32 Digital Camera Memory Interface * @@ -5,7 +6,6 @@ * Authors: Yannick Fertre <yannick.fertre@st.com> * Hugues Fruchet <hugues.fruchet@st.com> * for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 * * This driver is based on atmel_isi.c * @@ -93,6 +93,11 @@ enum state { #define MIN_HEIGHT 16U #define MAX_HEIGHT 2048U +#define MIN_JPEG_WIDTH 16U +#define MAX_JPEG_WIDTH 2592U +#define MIN_JPEG_HEIGHT 16U +#define MAX_JPEG_HEIGHT 2592U + #define TIMEOUT_MS 1000 struct dcmi_graph_entity { @@ -160,6 +165,7 @@ struct stm32_dcmi { dma_cookie_t dma_cookie; u32 misr; int errors_count; + int overrun_count; int buffers_count; }; @@ -190,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) static int dcmi_start_capture(struct stm32_dcmi *dcmi); +static void dcmi_buffer_done(struct stm32_dcmi *dcmi, + struct dcmi_buf *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + if (!buf) + return; + + vbuf = &buf->vb; + + vbuf->sequence = dcmi->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n", + vbuf->vb2_buf.index, vbuf->sequence, bytesused); + + dcmi->buffers_count++; + dcmi->active = NULL; +} + +static int dcmi_restart_capture(struct stm32_dcmi *dcmi) +{ + spin_lock_irq(&dcmi->irqlock); + + if (dcmi->state != RUNNING) { + spin_unlock_irq(&dcmi->irqlock); + return -EINVAL; + } + + /* Restart a new DMA transfer with next buffer */ + if (list_empty(&dcmi->buffers)) { + dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", + __func__); + dcmi->errors_count++; + dcmi->active = NULL; + + spin_unlock_irq(&dcmi->irqlock); + return -EINVAL; + } + + dcmi->active = list_entry(dcmi->buffers.next, + struct dcmi_buf, list); + list_del_init(&dcmi->active->list); + + spin_unlock_irq(&dcmi->irqlock); + + return dcmi_start_capture(dcmi); +} + static void dcmi_dma_callback(void *param) { struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; struct dma_chan *chan = dcmi->dma_chan; struct dma_tx_state state; enum dma_status status; - - spin_lock(&dcmi->irqlock); + struct dcmi_buf *buf = dcmi->active; /* Check DMA status */ status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); @@ -215,58 +274,18 @@ static void dcmi_dma_callback(void *param) case DMA_COMPLETE: dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); - if (dcmi->active) { - struct dcmi_buf *buf = dcmi->active; - struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb; - - vbuf->sequence = dcmi->sequence++; - vbuf->field = V4L2_FIELD_NONE; - vbuf->vb2_buf.timestamp = ktime_get_ns(); - vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size); - vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); - dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n", - vbuf->vb2_buf.index, vbuf->sequence); - - dcmi->buffers_count++; - dcmi->active = NULL; - } - - /* Restart a new DMA transfer with next buffer */ - if (dcmi->state == RUNNING) { - if (list_empty(&dcmi->buffers)) { - dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer", - __func__); - dcmi->errors_count++; - dcmi->active = NULL; - - spin_unlock(&dcmi->irqlock); - return; - } - - dcmi->active = list_entry(dcmi->buffers.next, - struct dcmi_buf, list); - - list_del_init(&dcmi->active->list); - - if (dcmi_start_capture(dcmi)) { - dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete", - __func__); - - spin_unlock(&dcmi->irqlock); - return; - } - - /* Enable capture */ - reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE); - } + /* Return buffer to V4L2 */ + dcmi_buffer_done(dcmi, buf, buf->size, 0); + /* Restart capture */ + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", + __func__); break; default: dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); break; } - - spin_unlock(&dcmi->irqlock); } static int dcmi_start_dma(struct stm32_dcmi *dcmi, @@ -359,11 +378,57 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi) reg_set(dcmi->regs, DCMI_CR, CR_CROP); } +static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) +{ + struct dma_tx_state state; + enum dma_status status; + struct dma_chan *chan = dcmi->dma_chan; + struct dcmi_buf *buf = dcmi->active; + + if (!buf) + return; + + /* + * Because of variable JPEG buffer size sent by sensor, + * DMA transfer never completes due to transfer size + * never reached. + * In order to ensure that all the JPEG data are transferred + * in active buffer memory, DMA is drained. + * Then DMA tx status gives the amount of data transferred + * to memory, which is then returned to V4L2 through the active + * buffer payload. + */ + + /* Drain DMA */ + dmaengine_synchronize(chan); + + /* Get DMA residue to get JPEG size */ + status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + if (status != DMA_ERROR && state.residue < buf->size) { + /* Return JPEG buffer to V4L2 with received JPEG buffer size */ + dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); + } else { + dcmi->errors_count++; + dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n", + __func__); + /* Return JPEG buffer to V4L2 in ERROR state */ + dcmi_buffer_done(dcmi, buf, 0, -EIO); + } + + /* Abort DMA operation */ + dmaengine_terminate_all(dcmi->dma_chan); + + /* Restart capture */ + if (dcmi_restart_capture(dcmi)) + dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n", + __func__); +} + static irqreturn_t dcmi_irq_thread(int irq, void *arg) { struct stm32_dcmi *dcmi = arg; - spin_lock(&dcmi->irqlock); + spin_lock_irq(&dcmi->irqlock); /* Stop capture is required */ if (dcmi->state == STOPPING) { @@ -373,50 +438,41 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) complete(&dcmi->complete); - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); return IRQ_HANDLED; } if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) { - /* - * An overflow or an error has been detected, - * stop current DMA transfert & restart it - */ - dev_warn(dcmi->dev, "%s: Overflow or error detected\n", - __func__); - dcmi->errors_count++; - dmaengine_terminate_all(dcmi->dma_chan); - - reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR); - - dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n"); - - if (dcmi_start_capture(dcmi)) { - dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", - __func__); + if (dcmi->misr & IT_OVR) + dcmi->overrun_count++; + } - spin_unlock(&dcmi->irqlock); - return IRQ_HANDLED; - } + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG && + dcmi->misr & IT_FRAME) { + /* JPEG received */ + spin_unlock_irq(&dcmi->irqlock); + dcmi_process_jpeg(dcmi); + return IRQ_HANDLED; } - spin_unlock(&dcmi->irqlock); + spin_unlock_irq(&dcmi->irqlock); return IRQ_HANDLED; } static irqreturn_t dcmi_irq_callback(int irq, void *arg) { struct stm32_dcmi *dcmi = arg; + unsigned long flags; - spin_lock(&dcmi->irqlock); + spin_lock_irqsave(&dcmi->irqlock, flags); dcmi->misr = reg_read(dcmi->regs, DCMI_MIS); /* Clear interrupt */ reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR); - spin_unlock(&dcmi->irqlock); + spin_unlock_irqrestore(&dcmi->irqlock, flags); return IRQ_WAKE_THREAD; } @@ -483,7 +539,7 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); - dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n", + dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n", vb->index, &buf->paddr, buf->size); } @@ -495,29 +551,24 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); - unsigned long flags = 0; - spin_lock_irqsave(&dcmi->irqlock, flags); + spin_lock_irq(&dcmi->irqlock); - if ((dcmi->state == RUNNING) && (!dcmi->active)) { + if (dcmi->state == RUNNING && !dcmi->active) { dcmi->active = buf; dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); - if (dcmi_start_capture(dcmi)) { + spin_unlock_irq(&dcmi->irqlock); + if (dcmi_start_capture(dcmi)) dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", __func__); - - spin_unlock_irqrestore(&dcmi->irqlock, flags); - return; - } } else { /* Enqueue to video buffers list */ list_add_tail(&buf->list, &dcmi->buffers); + spin_unlock_irq(&dcmi->irqlock); } - - spin_unlock_irqrestore(&dcmi->irqlock, flags); } static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -529,7 +580,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) ret = clk_enable(dcmi->mclk); if (ret) { - dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock", + dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n", __func__); goto err_release_buffers; } @@ -578,6 +629,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (dcmi->do_crop) dcmi_set_crop(dcmi); + /* Enable jpeg capture */ + if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) + reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ + /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); @@ -585,6 +640,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dcmi->sequence = 0; dcmi->errors_count = 0; + dcmi->overrun_count = 0; dcmi->buffers_count = 0; dcmi->active = NULL; @@ -603,20 +659,17 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + spin_unlock_irq(&dcmi->irqlock); ret = dcmi_start_capture(dcmi); if (ret) { - dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture", + dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); - - spin_unlock_irq(&dcmi->irqlock); goto err_subdev_streamoff; } /* Enable interruptions */ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); - spin_unlock_irq(&dcmi->irqlock); - return 0; err_subdev_streamoff: @@ -656,9 +709,12 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* Disable stream on the sub device */ ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); if (ret && ret != -ENOIOCTLCMD) - dev_err(dcmi->dev, "stream off failed in subdev\n"); + dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n", + __func__, ret); + spin_lock_irq(&dcmi->irqlock); dcmi->state = STOPPING; + spin_unlock_irq(&dcmi->irqlock); timeout = wait_for_completion_interruptible_timeout(&dcmi->complete, time_ms); @@ -672,7 +728,8 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); if (!timeout) { - dev_err(dcmi->dev, "Timeout during stop streaming\n"); + dev_err(dcmi->dev, "%s: Timeout during stop streaming\n", + __func__); dcmi->state = STOPPED; } @@ -694,8 +751,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) clk_disable(dcmi->mclk); - dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n", - dcmi->errors_count, dcmi->buffers_count); + if (dcmi->errors_count) + dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", + dcmi->errors_count, dcmi->overrun_count, + dcmi->buffers_count); + dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), buffers=%d\n", + dcmi->errors_count, dcmi->overrun_count, + dcmi->buffers_count); } static const struct vb2_ops dcmi_video_qops = { @@ -749,7 +811,7 @@ static void __find_outer_frame_size(struct stm32_dcmi *dcmi, int h_err = (fsize->height - pix->height); int err = w_err + h_err; - if ((w_err >= 0) && (h_err >= 0) && (err < min_err)) { + if (w_err >= 0 && h_err >= 0 && err < min_err) { min_err = err; match = fsize; } @@ -771,6 +833,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; + bool do_crop; int ret; sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); @@ -780,10 +843,19 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } /* Limit to hardware capabilities */ - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH); + pix->height = + clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT); + } else { + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + } - if (dcmi->do_crop && dcmi->num_of_sd_framesizes) { + /* No crop if JPEG is requested */ + do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG); + + if (do_crop && dcmi->num_of_sd_framesizes) { struct dcmi_framesize outer_sd_fsize; /* * If crop is requested and sensor have discrete frame sizes, @@ -807,7 +879,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, sd_fsize.width = pix->width; sd_fsize.height = pix->height; - if (dcmi->do_crop) { + if (do_crop) { struct v4l2_rect c = dcmi->crop; struct v4l2_rect max_rect; @@ -862,6 +934,10 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) if (ret) return ret; + /* Disable crop if JPEG is requested */ + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) + dcmi->do_crop = false; + /* pix to mbus format */ v4l2_fill_mbus_format(mf, pix, sd_format->mbus_code); @@ -1084,10 +1160,10 @@ static int dcmi_s_selection(struct file *file, void *priv, r.top = clamp_t(s32, r.top, 0, pix.height - r.height); r.left = clamp_t(s32, r.left, 0, pix.width - r.width); - if (!((r.top == dcmi->sd_bounds.top) && - (r.left == dcmi->sd_bounds.left) && - (r.width == dcmi->sd_bounds.width) && - (r.height == dcmi->sd_bounds.height))) { + if (!(r.top == dcmi->sd_bounds.top && + r.left == dcmi->sd_bounds.left && + r.width == dcmi->sd_bounds.width && + r.height == dcmi->sd_bounds.height)) { /* Crop if request is different than sensor resolution */ dcmi->do_crop = true; dcmi->crop = r; @@ -1167,6 +1243,22 @@ static int dcmi_enum_framesizes(struct file *file, void *fh, return 0; } +static int dcmi_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p); +} + +static int dcmi_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stm32_dcmi *dcmi = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p); +} + static int dcmi_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) { @@ -1269,6 +1361,9 @@ static const struct v4l2_ioctl_ops dcmi_ioctl_ops = { .vidioc_g_input = dcmi_g_input, .vidioc_s_input = dcmi_s_input, + .vidioc_g_parm = dcmi_g_parm, + .vidioc_s_parm = dcmi_s_parm, + .vidioc_enum_framesizes = dcmi_enum_framesizes, .vidioc_enum_frameintervals = dcmi_enum_frameintervals, @@ -1334,6 +1429,10 @@ static const struct dcmi_format dcmi_formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_JPEG, + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + .bpp = 1, }, }; diff --git a/drivers/media/platform/tegra-cec/tegra_cec.c b/drivers/media/platform/tegra-cec/tegra_cec.c index 92f93a880015..aba488cd0e64 100644 --- a/drivers/media/platform/tegra-cec/tegra_cec.c +++ b/drivers/media/platform/tegra-cec/tegra_cec.c @@ -172,16 +172,13 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) } } - if (status & (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | - TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | - TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | - TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { + if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) { cec_write(cec, TEGRA_CEC_INT_STAT, - (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | - TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | - TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | - TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)); - } else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { + TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED); + cec->rx_done = false; + cec->rx_buf_cnt = 0; + } + if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { u32 v; cec_write(cec, TEGRA_CEC_INT_STAT, @@ -255,7 +252,7 @@ static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable) TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED | TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | - TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN); + TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED); cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE); return 0; diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index f77be9302120..e9a02639554b 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -1112,7 +1112,7 @@ static int viacam_g_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->lock); - ret = sensor_call(cam, video, g_parm, parm); + ret = v4l2_g_parm_cap(video_devdata(filp), cam->sensor, parm); mutex_unlock(&cam->lock); parm->parm.capture.readbuffers = cam->n_cap_bufs; return ret; @@ -1125,7 +1125,7 @@ static int viacam_s_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->lock); - ret = sensor_call(cam, video, s_parm, parm); + ret = v4l2_s_parm_cap(video_devdata(filp), cam->sensor, parm); mutex_unlock(&cam->lock); parm->parm.capture.readbuffers = cam->n_cap_bufs; return ret; diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 9d63c84a9876..617415c224fe 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -434,7 +434,9 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, v4l2_set_subdevdata(sd, ved); /* Expose this subdev to user space */ - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (sd->ctrl_handler) + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; /* Initialize the media entity */ ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 4d663e89d33f..6e10b63ba9ec 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -533,7 +533,7 @@ static int vimc_deb_comp_bind(struct device *comp, struct device *master, /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, pdata->entity_name, - MEDIA_ENT_F_ATV_DECODER, 2, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, (const unsigned long[2]) {MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE}, &vimc_deb_ops); diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index e1602e0bc230..e583ec7a91da 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -395,7 +395,7 @@ static int vimc_sca_comp_bind(struct device *comp, struct device *master, /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, pdata->entity_name, - MEDIA_ENT_F_ATV_DECODER, 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, (const unsigned long[2]) {MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE}, &vimc_sca_ops); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 457e211514c6..605e2a2d5dd5 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -23,6 +23,7 @@ #include <linux/v4l2-mediabus.h> #include <linux/vmalloc.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/v4l2-subdev.h> #include <media/tpg/v4l2-tpg.h> @@ -284,11 +285,18 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) return 0; } +static struct v4l2_subdev_core_ops vimc_sen_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops vimc_sen_video_ops = { .s_stream = vimc_sen_s_stream, }; static const struct v4l2_subdev_ops vimc_sen_ops = { + .core = &vimc_sen_core_ops, .pad = &vimc_sen_pad_ops, .video = &vimc_sen_video_ops, }; @@ -378,7 +386,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, pdata->entity_name, - MEDIA_ENT_F_ATV_DECODER, 1, + MEDIA_ENT_F_CAM_SENSOR, 1, (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, &vimc_sen_ops); if (ret) diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c index b55d278d38a7..71105fa4c5f9 100644 --- a/drivers/media/platform/vivid/vivid-cec.c +++ b/drivers/media/platform/vivid/vivid-cec.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-cec.c - A Virtual Video Test Driver, cec emulation * * 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 <media/cec.h> @@ -82,11 +70,18 @@ static void vivid_cec_pin_adap_events(struct cec_adapter *adap, ktime_t ts, if (adap == NULL) return; - ts = ktime_sub_us(ts, (CEC_TIM_START_BIT_TOTAL + - len * 10 * CEC_TIM_DATA_BIT_TOTAL)); - cec_queue_pin_cec_event(adap, false, ts); + + /* + * Suffix ULL on constant 10 makes the expression + * CEC_TIM_START_BIT_TOTAL + 10ULL * len * CEC_TIM_DATA_BIT_TOTAL + * to be evaluated using 64-bit unsigned arithmetic (u64), which + * is what ktime_sub_us expects as second argument. + */ + ts = ktime_sub_us(ts, CEC_TIM_START_BIT_TOTAL + + 10ULL * len * CEC_TIM_DATA_BIT_TOTAL); + cec_queue_pin_cec_event(adap, false, false, ts); ts = ktime_add_us(ts, CEC_TIM_START_BIT_LOW); - cec_queue_pin_cec_event(adap, true, ts); + cec_queue_pin_cec_event(adap, true, false, ts); ts = ktime_add_us(ts, CEC_TIM_START_BIT_HIGH); for (i = 0; i < 10 * len; i++) { @@ -101,12 +96,12 @@ static void vivid_cec_pin_adap_events(struct cec_adapter *adap, ktime_t ts, bit = cec_msg_is_broadcast(msg) ^ nacked; break; } - cec_queue_pin_cec_event(adap, false, ts); + cec_queue_pin_cec_event(adap, false, false, ts); if (bit) ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_LOW); else ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_0_LOW); - cec_queue_pin_cec_event(adap, true, ts); + cec_queue_pin_cec_event(adap, true, false, ts); if (bit) ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_HIGH); else diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/platform/vivid/vivid-cec.h index 3926b1422777..7524ed48a914 100644 --- a/drivers/media/platform/vivid/vivid-cec.h +++ b/drivers/media/platform/vivid/vivid-cec.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-cec.h - A Virtual Video Test Driver, cec emulation * * 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. */ #ifdef CONFIG_VIDEO_VIVID_CEC diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 9f036c5f51b0..82ec216f2ad8 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-core.c - A Virtual Video Test Driver, core initialization * * Copyright 2014 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 <linux/module.h> diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index c90e4a0ab94e..477c80a4d44c 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-core.h - core datastructures * * Copyright 2014 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. */ #ifndef _VIVID_CORE_H_ diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 3f9d354827af..6b0bfa091592 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-ctrls.c - control support functions. * * Copyright 2014 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 <linux/errno.h> @@ -1208,6 +1196,7 @@ static int vivid_radio_rx_s_ctrl(struct v4l2_ctrl *ctrl) v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls); v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls); v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls); + dev->radio_rx_dev.device_caps = dev->radio_rx_caps; break; case V4L2_CID_RDS_RECEPTION: dev->radio_rx_rds_enabled = ctrl->val; @@ -1282,6 +1271,7 @@ static int vivid_radio_tx_s_ctrl(struct v4l2_ctrl *ctrl) dev->radio_tx_caps &= ~V4L2_CAP_READWRITE; if (!dev->radio_tx_rds_controls) dev->radio_tx_caps |= V4L2_CAP_READWRITE; + dev->radio_tx_dev.device_caps = dev->radio_tx_caps; break; case V4L2_CID_RDS_TX_PTY: if (dev->radio_rx_rds_controls) diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h index 9bcca9d56359..6fad5f5d0054 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.h +++ b/drivers/media/platform/vivid/vivid-ctrls.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-ctrls.h - control support functions. * * Copyright 2014 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. */ #ifndef _VIVID_CTRLS_H_ diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 6ca71aabb576..3fdb280c36ca 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-kthread-cap.h - video/vbi capture thread support functions. * * Copyright 2014 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 <linux/module.h> diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h index 5b92fc9a0d04..0f43015306d6 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.h +++ b/drivers/media/platform/vivid/vivid-kthread-cap.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-kthread-cap.h - video/vbi capture thread support functions. * * Copyright 2014 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. */ #ifndef _VIVID_KTHREAD_CAP_H_ diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c index 98eed5889bc1..9981e7548019 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.c +++ b/drivers/media/platform/vivid/vivid-kthread-out.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-kthread-out.h - video/vbi output thread support functions. * * Copyright 2014 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 <linux/module.h> diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h index 2bf04a17b05d..d5bcf44bbaca 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.h +++ b/drivers/media/platform/vivid/vivid-kthread-out.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-kthread-out.h - video/vbi output thread support functions. * * Copyright 2014 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. */ #ifndef _VIVID_KTHREAD_OUT_H_ diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c index bdc380b14e0c..bbbc1b6938a5 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-osd.c - osd support for testing overlays. * * Copyright 2014 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 <linux/module.h> diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/platform/vivid/vivid-osd.h index 57c9daa5940a..f9ac1af25dd3 100644 --- a/drivers/media/platform/vivid/vivid-osd.h +++ b/drivers/media/platform/vivid/vivid-osd.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-osd.h - output overlay support functions. * * Copyright 2014 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. */ #ifndef _VIVID_OSD_H_ diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c index 78c1e920670a..7c8efe38ff5b 100644 --- a/drivers/media/platform/vivid/vivid-radio-common.c +++ b/drivers/media/platform/vivid/vivid-radio-common.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-radio-common.c - common radio rx/tx support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/platform/vivid/vivid-radio-common.h index 92fe589141b7..30a9900e5b2b 100644 --- a/drivers/media/platform/vivid/vivid-radio-common.h +++ b/drivers/media/platform/vivid/vivid-radio-common.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-radio-common.h - common radio rx/tx support functions. * * Copyright 2014 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. */ #ifndef _VIVID_RADIO_COMMON_H_ diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c index f834f7df8cf9..1f86d7d4f72f 100644 --- a/drivers/media/platform/vivid/vivid-radio-rx.c +++ b/drivers/media/platform/vivid/vivid-radio-rx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-radio-rx.c - radio receiver support functions. * * Copyright 2014 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 <linux/errno.h> @@ -259,7 +247,7 @@ int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) vt->rangehigh = FM_FREQ_RANGE_HIGH; sig_qual = dev->radio_rx_sig_qual; vt->signal = abs(sig_qual) > delta ? 0 : - 0xffff - (abs(sig_qual) * 0xffff) / delta; + 0xffff - ((unsigned)abs(sig_qual) * 0xffff) / delta; vt->afc = sig_qual > delta ? 0 : sig_qual; if (abs(sig_qual) > delta) vt->rxsubchans = 0; diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/platform/vivid/vivid-radio-rx.h index 2b33edb60942..c9c7849f6f99 100644 --- a/drivers/media/platform/vivid/vivid-radio-rx.h +++ b/drivers/media/platform/vivid/vivid-radio-rx.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-radio-rx.h - radio receiver support functions. * * Copyright 2014 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. */ #ifndef _VIVID_RADIO_RX_H_ diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c index 308b13f85dc0..1a3749ba5e7e 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.c +++ b/drivers/media/platform/vivid/vivid-radio-tx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-radio-tx.c - radio transmitter support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h index 3c3343d70cbc..c2bf1e7e634a 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.h +++ b/drivers/media/platform/vivid/vivid-radio-tx.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-radio-tx.h - radio transmitter support functions. * * Copyright 2014 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. */ #ifndef _VIVID_RADIO_TX_H_ diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c index 996e35e28d37..39ca9a56448c 100644 --- a/drivers/media/platform/vivid/vivid-rds-gen.c +++ b/drivers/media/platform/vivid/vivid-rds-gen.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-rds-gen.c - rds (radio data system) generator support functions. * * Copyright 2014 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 <linux/kernel.h> diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/platform/vivid/vivid-rds-gen.h index e55e3b22b7ca..35ac5742302b 100644 --- a/drivers/media/platform/vivid/vivid-rds-gen.h +++ b/drivers/media/platform/vivid/vivid-rds-gen.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-rds-gen.h - rds (radio data system) generator support functions. * * Copyright 2014 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. */ #ifndef _VIVID_RDS_GEN_H_ diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c index ebd7b9c4dd83..cfb7cb4d37a8 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-sdr-cap.c - software defined radio support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h index 43014b2733db..813c9248e5a7 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.h +++ b/drivers/media/platform/vivid/vivid-sdr-cap.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-sdr-cap.h - software defined radio support functions. * * Copyright 2014 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. */ #ifndef _VIVID_SDR_CAP_H_ diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c index d66ef95dd2b5..92a852955173 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.c +++ b/drivers/media/platform/vivid/vivid-vbi-cap.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vbi-cap.c - vbi capture support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/platform/vivid/vivid-vbi-cap.h index 2d8ea0bac743..91d2de01381c 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.h +++ b/drivers/media/platform/vivid/vivid-vbi-cap.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vbi-cap.h - vbi capture support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VBI_CAP_H_ diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/platform/vivid/vivid-vbi-gen.c index 02c79d7cedab..acc98445a1fa 100644 --- a/drivers/media/platform/vivid/vivid-vbi-gen.c +++ b/drivers/media/platform/vivid/vivid-vbi-gen.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vbi-gen.c - vbi generator support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/platform/vivid/vivid-vbi-gen.h index 8444abe905ea..2657a7f5571c 100644 --- a/drivers/media/platform/vivid/vivid-vbi-gen.h +++ b/drivers/media/platform/vivid/vivid-vbi-gen.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vbi-gen.h - vbi generator support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VBI_GEN_H_ diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c index d2989195cf03..69486c130a7e 100644 --- a/drivers/media/platform/vivid/vivid-vbi-out.c +++ b/drivers/media/platform/vivid/vivid-vbi-out.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vbi-out.c - vbi output support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/platform/vivid/vivid-vbi-out.h index 6555ba9d2860..76584940cdaf 100644 --- a/drivers/media/platform/vivid/vivid-vbi-out.h +++ b/drivers/media/platform/vivid/vivid-vbi-out.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vbi-out.h - vbi output support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VBI_OUT_H_ diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 0fbbcde19f0d..01c703683657 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vid-cap.c - video capture support functions. * * Copyright 2014 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 <linux/errno.h> @@ -573,9 +561,8 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, mp->field = vivid_field_cap(dev, mp->field); if (vivid_is_webcam(dev)) { const struct v4l2_frmsize_discrete *sz = - v4l2_find_nearest_format(webcam_sizes, - VIVID_WEBCAM_SIZES, - mp->width, mp->height); + v4l2_find_nearest_size(webcam_sizes, width, height, + mp->width, mp->height); w = sz->width; h = sz->height; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/platform/vivid/vivid-vid-cap.h index 94079815dbc2..47d8b48820df 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.h +++ b/drivers/media/platform/vivid/vivid-vid-cap.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vid-cap.h - video capture support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VID_CAP_H_ diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index a651527d80db..e5914be0e12d 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vid-common.c - common video support functions. * * Copyright 2014 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 <linux/errno.h> @@ -874,7 +862,8 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; if (edid->start_block + edid->blocks > dev->edid_blocks) edid->blocks = dev->edid_blocks - edid->start_block; - cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); + if (adap) + cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); return 0; } diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h index 4b6175eab8a2..29b6c0b40a1b 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.h +++ b/drivers/media/platform/vivid/vivid-vid-common.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vid-common.h - common video support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VID_COMMON_H_ diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 0b1b6218ede8..51fec66d8d45 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vivid-vid-out.c - video output support functions. * * Copyright 2014 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 <linux/errno.h> diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h index dfa84db184ed..e87aacf843c5 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.h +++ b/drivers/media/platform/vivid/vivid-vid-out.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vivid-vid-out.h - video output support functions. * * Copyright 2014 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. */ #ifndef _VIVID_VID_OUT_H_ diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 4257451f1bd8..0b86ed01e85d 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -509,7 +509,8 @@ static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) return !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD); else - return !!(vsp1_read(vsp1, VI6_CMD(dlm->index) & VI6_CMD_UPDHDR)); + return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) + & VI6_CMD_UPDHDR); } static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 7ce69f23f50a..b8fee1834253 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -27,6 +27,7 @@ #include "vsp1_pipe.h" #include "vsp1_rwpf.h" +#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS" /* ----------------------------------------------------------------------------- * Interrupt Handling @@ -88,7 +89,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, struct vsp1_entity *next; struct vsp1_dl_list *dl; struct v4l2_subdev_format format; - const char *bru_name; unsigned long flags; unsigned int i; int ret; @@ -99,7 +99,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; bru = to_bru(&pipe->bru->subdev); - bru_name = pipe->bru->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"; if (!cfg) { /* @@ -165,7 +164,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", __func__, format.format.width, format.format.height, - format.format.code, bru_name, i); + format.format.code, BRU_NAME(pipe->bru), i); } format.pad = pipe->bru->source_pad; @@ -181,7 +180,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", __func__, format.format.width, format.format.height, - format.format.code, bru_name, i); + format.format.code, BRU_NAME(pipe->bru), i); format.pad = RWPF_PAD_SINK; ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL, @@ -473,9 +472,9 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, if (ret < 0) return ret; - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n", + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", __func__, format.format.width, format.format.height, - format.format.code, format.pad); + format.format.code, BRU_NAME(pipe->bru), format.pad); sel.pad = bru_input; sel.target = V4L2_SEL_TGT_COMPOSE; @@ -486,10 +485,9 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, if (ret < 0) return ret; - dev_dbg(vsp1->dev, - "%s: set selection (%u,%u)/%ux%u on BRU pad %u\n", + dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n", __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, - sel.pad); + BRU_NAME(pipe->bru), sel.pad); return 0; } @@ -514,12 +512,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index) struct vsp1_entity *entity; struct vsp1_entity *next; struct vsp1_dl_list *dl; - const char *bru_name; unsigned int i; int ret; - bru_name = pipe->bru->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"; - /* Prepare the display list. */ dl = vsp1_dl_list_get(pipe->output->dlm); @@ -530,6 +525,15 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index) struct vsp1_rwpf *rpf = vsp1->rpf[i]; unsigned int j; + /* + * Make sure we don't accept more inputs than the hardware can + * handle. This is a temporary fix to avoid display stall, we + * need to instead allocate the BRU or BRS to display pipelines + * dynamically based on the number of planes they each use. + */ + if (pipe->num_inputs >= pipe->bru->source_pad) + pipe->inputs[i] = NULL; + if (!pipe->inputs[i]) continue; @@ -561,7 +565,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index) rpf->entity.sink_pad = i; dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n", - __func__, rpf->entity.index, bru_name, i); + __func__, rpf->entity.index, BRU_NAME(pipe->bru), i); ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index e6fa16d7fda8..704920753998 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -155,6 +155,18 @@ static void lif_configure(struct vsp1_entity *entity, (obth << VI6_LIF_CTRL_OBTH_SHIFT) | (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); + + /* + * On R-Car V3M the LIF0 buffer attribute register has to be set to a + * non-default value to guarantee proper operation (otherwise artifacts + * may appear on the output). The value required by the manual is not + * explained but is likely a buffer size or threshold. + */ + if ((entity->vsp1->version & VI6_IP_VERSION_MASK) == + (VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M)) + vsp1_lif_write(lif, dl, VI6_LIF_LBA, + VI6_LIF_LBA_LBA0 | + (1536 << VI6_LIF_LBA_LBA1_SHIFT)); } static const struct vsp1_entity_operations lif_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 26c4ffad2f46..dae0c1901297 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -225,7 +225,7 @@ #define VI6_RPF_MULT_ALPHA_P_MMD_RATIO (1 << 8) #define VI6_RPF_MULT_ALPHA_P_MMD_IMAGE (2 << 8) #define VI6_RPF_MULT_ALPHA_P_MMD_BOTH (3 << 8) -#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff < 0) +#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff << 0) #define VI6_RPF_MULT_ALPHA_RATIO_SHIFT 0 /* ----------------------------------------------------------------------------- @@ -693,6 +693,11 @@ #define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0) #define VI6_LIF_CSBTH_LBTH_SHIFT 0 +#define VI6_LIF_LBA 0x3b0c +#define VI6_LIF_LBA_LBA0 (1 << 31) +#define VI6_LIF_LBA_LBA1_MASK (0xfff << 16) +#define VI6_LIF_LBA_LBA1_SHIFT 16 + /* ----------------------------------------------------------------------------- * Security Control Registers */ @@ -705,6 +710,7 @@ */ #define VI6_IP_VERSION 0x3f00 +#define VI6_IP_VERSION_MASK (0xffff << 0) #define VI6_IP_VERSION_MODEL_MASK (0xff << 8) #define VI6_IP_VERSION_MODEL_VSPS_H2 (0x09 << 8) #define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index dc6c4f985911..0f292c6ba338 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -511,7 +511,7 @@ static int usb_amradio_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct amradio_device *radio; - int retval = 0; + int retval; radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL); diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c index 70a2c86774ce..9a5079d64c4a 100644 --- a/drivers/media/radio/radio-raremono.c +++ b/drivers/media/radio/radio-raremono.c @@ -1,18 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2013 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 <linux/kernel.h> diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 58e944591602..8f9f8dfc3497 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -671,7 +671,7 @@ fail: static int wl1273_fm_suspend(struct wl1273_device *radio) { struct wl1273_core *core = radio->core; - int r = 0; + int r; /* Cannot go from OFF to SUSPENDED */ if (core->mode == WL1273_MODE_RX) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index e0054e0f410d..b94d66e53d4e 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -207,6 +207,15 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) unsigned long time_left; bool timed_out = false; + retval = si470x_get_register(radio, POWERCFG); + if (retval) + return retval; + + if ((radio->registers[POWERCFG] & (POWERCFG_ENABLE|POWERCFG_DMUTE)) + != (POWERCFG_ENABLE|POWERCFG_DMUTE)) { + return 0; + } + /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; @@ -377,8 +386,12 @@ int si470x_start(struct si470x_device *radio) goto done; /* sysconfig 1 */ - radio->registers[SYSCONFIG1] = - (de << 11) & SYSCONFIG1_DE; /* DE*/ + radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN | SYSCONFIG1_STCIEN | + SYSCONFIG1_RDS; + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2; + radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT; + if (de) + radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE; retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) goto done; diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index b3034f80163f..41709b24b28f 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -43,7 +43,6 @@ static const struct i2c_device_id si470x_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); - /************************************************************************** * Module Parameters **************************************************************************/ @@ -362,22 +361,43 @@ static int si470x_i2c_probe(struct i2c_client *client, mutex_init(&radio->lock); init_completion(&radio->completion); + retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&client->dev, "couldn't register v4l2_device\n"); + goto err_radio; + } + + v4l2_ctrl_handler_init(&radio->hdl, 2); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&client->dev, "couldn't register control\n"); + goto err_dev; + } + /* video device initialization */ radio->videodev = si470x_viddev_template; + radio->videodev.ctrl_handler = &radio->hdl; + radio->videodev.lock = &radio->lock; + radio->videodev.v4l2_dev = &radio->v4l2_dev; + radio->videodev.release = video_device_release_empty; video_set_drvdata(&radio->videodev, radio); /* power up : need 110ms */ radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { retval = -EIO; - goto err_radio; + goto err_ctrl; } msleep(110); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_radio; + goto err_ctrl; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[SI_CHIPID]); @@ -407,7 +427,7 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_radio; + goto err_ctrl; } /* rds buffer configuration */ @@ -437,6 +457,10 @@ err_all: free_irq(client->irq, radio); err_rds: kfree(radio->buffer); +err_ctrl: + v4l2_ctrl_handler_free(&radio->hdl); +err_dev: + v4l2_device_unregister(&radio->v4l2_dev); err_radio: kfree(radio); err_initial: diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index c311f9951d80..2277e850bb5e 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -578,7 +578,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, struct si470x_device *radio; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int i, int_end_size, retval = 0; + int i, int_end_size, retval; unsigned char version_warning = 0; /* private data allocation and initialization */ diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index eb7b834a0ae5..0202f8eb90c4 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -79,6 +79,8 @@ #define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ #define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ #define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ +#define SYSCONFIG1_GPIO2_DIS 0x0000 /* Disable GPIO 2 interrupt */ +#define SYSCONFIG1_GPIO2_INT 0x0004 /* Enable STC/RDS interrupt */ #define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ #define SYSCONFIG2 5 /* System Configuration 2 */ diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index a115db24667b..05c66701a899 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2013 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. */ /* kernel includes */ diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index f14ead5954e0..eb2c3b6eca7f 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -28,14 +28,12 @@ config LIRC menuconfig RC_DECODERS bool "Remote controller decoders" depends on RC_CORE - default y if RC_DECODERS config IR_NEC_DECODER tristate "Enable IR raw decoder for the NEC protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have IR with NEC protocol, and @@ -45,7 +43,6 @@ config IR_RC5_DECODER tristate "Enable IR raw decoder for the RC-5 protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have IR with RC-5 protocol, and @@ -55,7 +52,6 @@ config IR_RC6_DECODER tristate "Enable IR raw decoder for the RC6 protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have an infrared remote control which @@ -65,7 +61,6 @@ config IR_JVC_DECODER tristate "Enable IR raw decoder for the JVC protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have an infrared remote control which @@ -75,7 +70,6 @@ config IR_SONY_DECODER tristate "Enable IR raw decoder for the Sony protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have an infrared remote control which @@ -84,7 +78,6 @@ config IR_SONY_DECODER config IR_SANYO_DECODER tristate "Enable IR raw decoder for the Sanyo protocol" depends on RC_CORE - default y ---help--- Enable this option if you have an infrared remote control which @@ -94,7 +87,6 @@ config IR_SANYO_DECODER config IR_SHARP_DECODER tristate "Enable IR raw decoder for the Sharp protocol" depends on RC_CORE - default y ---help--- Enable this option if you have an infrared remote control which @@ -105,7 +97,6 @@ config IR_MCE_KBD_DECODER tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have a Microsoft Remote Keyboard for @@ -116,11 +107,19 @@ config IR_XMP_DECODER tristate "Enable IR raw decoder for the XMP protocol" depends on RC_CORE select BITREVERSE - default y ---help--- Enable this option if you have IR with XMP protocol, and if the IR is decoded in software + +config IR_IMON_DECODER + tristate "Enable IR raw decoder for the iMON protocol" + depends on RC_CORE + ---help--- + Enable this option if you have iMON PAD or Antec Veris infrared + remote control and you would like to use it with a raw IR + receiver, or if you wish to use an encoder to transmit this IR. + endif #RC_DECODERS menuconfig RC_DEVICES @@ -185,6 +184,18 @@ config IR_IMON To compile this driver as a module, choose M here: the module will be called imon. +config IR_IMON_RAW + tristate "SoundGraph iMON Receiver (early raw IR models)" + depends on USB_ARCH_HAS_HCD + depends on RC_CORE + select USB + ---help--- + Say Y here if you want to use a SoundGraph iMON IR Receiver, + early raw models. + + To compile this driver as a module, choose M here: the + module will be called imon_raw. + config IR_MCEUSB tristate "Windows Media Center Ed. eHome Infrared Transceiver" depends on USB_ARCH_HAS_HCD @@ -446,7 +457,6 @@ config IR_SERIAL config IR_SERIAL_TRANSMITTER bool "Serial Port Transmitter" - default y depends on IR_SERIAL ---help--- Serial Port Transmitter support diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 0e857816ac2d..2e1c87066f6c 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -14,11 +14,13 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o +obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o obj-$(CONFIG_IR_IMON) += imon.o +obj-$(CONFIG_IR_IMON_RAW) += imon_raw.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o obj-$(CONFIG_IR_FINTEK) += fintek-cir.o diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index a896d3c83a1c..d2c6617d468e 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -1,7 +1,7 @@ config IR_IMG tristate "ImgTec IR Decoder" depends on RC_CORE - depends on METAG || MIPS || COMPILE_TEST + depends on MIPS || COMPILE_TEST select IR_IMG_HW if !IR_IMG_RAW help Say Y or M here if you want to use the ImgTec infrared decoder diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 950d068ba806..1041c056854d 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -92,7 +92,6 @@ struct imon_usb_dev_descr { __u16 flags; #define IMON_NO_FLAGS 0 #define IMON_NEED_20MS_PKT_DELAY 1 -#define IMON_IR_RAW 2 struct imon_panel_key_table key_table[]; }; @@ -123,12 +122,6 @@ struct imon_context { unsigned char usb_tx_buf[8]; unsigned int send_packet_delay; - struct rx_data { - int count; /* length of 0 or 1 sequence */ - int prev_bit; /* logic level of sequence */ - int initial_space; /* initial space flag */ - } rx; - struct tx_t { unsigned char data_buf[35]; /* user data buffer */ struct completion finished; /* wait for write to finish */ @@ -331,10 +324,6 @@ static const struct imon_usb_dev_descr imon_DH102 = { } }; -static const struct imon_usb_dev_descr imon_ir_raw = { - .flags = IMON_IR_RAW, -}; - /* * USB Device ID for iMON USB Control Boards * @@ -418,18 +407,6 @@ static const struct usb_device_id imon_usb_id_table[] = { /* device specifics unknown */ { USB_DEVICE(0x15c2, 0x0046), .driver_info = (unsigned long)&imon_default_table}, - /* TriGem iMON (IR only) -- TG_iMON.inf */ - { USB_DEVICE(0x0aa8, 0x8001), - .driver_info = (unsigned long)&imon_ir_raw}, - /* SoundGraph iMON (IR only) -- sg_imon.inf */ - { USB_DEVICE(0x04e8, 0xff30), - .driver_info = (unsigned long)&imon_ir_raw}, - /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */ - { USB_DEVICE(0x0aa8, 0xffda), - .driver_info = (unsigned long)&imon_ir_raw}, - /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */ - { USB_DEVICE(0x15c2, 0xffda), - .driver_info = (unsigned long)&imon_ir_raw}, {} }; @@ -1133,18 +1110,18 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) dev_dbg(dev, "Configuring IR receiver for MCE protocol\n"); ir_proto_packet[0] = 0x01; *rc_proto = RC_PROTO_BIT_RC6_MCE; - } else if (*rc_proto & RC_PROTO_BIT_OTHER) { + } else if (*rc_proto & RC_PROTO_BIT_IMON) { dev_dbg(dev, "Configuring IR receiver for iMON protocol\n"); if (!pad_stabilize) dev_dbg(dev, "PAD stabilize functionality disabled\n"); /* ir_proto_packet[0] = 0x00; // already the default */ - *rc_proto = RC_PROTO_BIT_OTHER; + *rc_proto = RC_PROTO_BIT_IMON; } else { dev_warn(dev, "Unsupported IR protocol specified, overriding to iMON IR protocol\n"); if (!pad_stabilize) dev_dbg(dev, "PAD stabilize functionality disabled\n"); /* ir_proto_packet[0] = 0x00; // already the default */ - *rc_proto = RC_PROTO_BIT_OTHER; + *rc_proto = RC_PROTO_BIT_IMON; } memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); @@ -1411,7 +1388,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) rel_x = buf[2]; rel_y = buf[3]; - if (ictx->rc_proto == RC_PROTO_BIT_OTHER && pad_stabilize) { + if (ictx->rc_proto == RC_PROTO_BIT_IMON && pad_stabilize) { if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) { dir = stabilize((int)rel_x, (int)rel_y, timeout, threshold); @@ -1478,7 +1455,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) buf[0] = 0x01; buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0; - if (ictx->rc_proto == RC_PROTO_BIT_OTHER && pad_stabilize) { + if (ictx->rc_proto == RC_PROTO_BIT_IMON && pad_stabilize) { dir = stabilize((int)rel_x, (int)rel_y, timeout, threshold); if (!dir) { @@ -1572,94 +1549,11 @@ static int imon_parse_press_type(struct imon_context *ictx, /* * Process the incoming packet */ -/* - * Convert bit count to time duration (in us) and submit - * the value to lirc_dev. - */ -static void submit_data(struct imon_context *context) -{ - DEFINE_IR_RAW_EVENT(ev); - - ev.pulse = context->rx.prev_bit; - ev.duration = US_TO_NS(context->rx.count * BIT_DURATION); - ir_raw_event_store_with_filter(context->rdev, &ev); -} - -/* - * Process the incoming packet - */ -static void imon_incoming_ir_raw(struct imon_context *context, +static void imon_incoming_packet(struct imon_context *ictx, struct urb *urb, int intf) { int len = urb->actual_length; unsigned char *buf = urb->transfer_buffer; - struct device *dev = context->dev; - int octet, bit; - unsigned char mask; - - if (len != 8) { - dev_warn(dev, "imon %s: invalid incoming packet size (len = %d, intf%d)\n", - __func__, len, intf); - return; - } - - if (debug) - dev_info(dev, "raw packet: %*ph\n", len, buf); - /* - * Translate received data to pulse and space lengths. - * Received data is active low, i.e. pulses are 0 and - * spaces are 1. - * - * My original algorithm was essentially similar to - * Changwoo Ryu's with the exception that he switched - * the incoming bits to active high and also fed an - * initial space to LIRC at the start of a new sequence - * if the previous bit was a pulse. - * - * I've decided to adopt his algorithm. - */ - - if (buf[7] == 1 && context->rx.initial_space) { - /* LIRC requires a leading space */ - context->rx.prev_bit = 0; - context->rx.count = 4; - submit_data(context); - context->rx.count = 0; - } - - for (octet = 0; octet < 5; ++octet) { - mask = 0x80; - for (bit = 0; bit < 8; ++bit) { - int curr_bit = !(buf[octet] & mask); - - if (curr_bit != context->rx.prev_bit) { - if (context->rx.count) { - submit_data(context); - context->rx.count = 0; - } - context->rx.prev_bit = curr_bit; - } - ++context->rx.count; - mask >>= 1; - } - } - - if (buf[7] == 10) { - if (context->rx.count) { - submit_data(context); - context->rx.count = 0; - } - context->rx.initial_space = context->rx.prev_bit; - } - - ir_raw_event_handle(context->rdev); -} - -static void imon_incoming_scancode(struct imon_context *ictx, - struct urb *urb, int intf) -{ - int len = urb->actual_length; - unsigned char *buf = urb->transfer_buffer; struct device *dev = ictx->dev; unsigned long flags; u32 kc; @@ -1745,11 +1639,18 @@ static void imon_incoming_scancode(struct imon_context *ictx, if (press_type == 0) rc_keyup(ictx->rdev); else { - if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE || - ictx->rc_proto == RC_PROTO_BIT_OTHER) - rc_keydown(ictx->rdev, - ictx->rc_proto == RC_PROTO_BIT_RC6_MCE ? RC_PROTO_RC6_MCE : RC_PROTO_OTHER, - ictx->rc_scancode, ictx->rc_toggle); + enum rc_proto proto; + + if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE) + proto = RC_PROTO_RC6_MCE; + else if (ictx->rc_proto == RC_PROTO_BIT_IMON) + proto = RC_PROTO_IMON; + else + return; + + rc_keydown(ictx->rdev, proto, ictx->rc_scancode, + ictx->rc_toggle); + spin_lock_irqsave(&ictx->kc_lock, flags); ictx->last_keycode = ictx->kc; spin_unlock_irqrestore(&ictx->kc_lock, flags); @@ -1839,10 +1740,7 @@ static void usb_rx_callback_intf0(struct urb *urb) break; case 0: - if (ictx->rdev->driver_type == RC_DRIVER_IR_RAW) - imon_incoming_ir_raw(ictx, urb, intfnum); - else - imon_incoming_scancode(ictx, urb, intfnum); + imon_incoming_packet(ictx, urb, intfnum); break; default: @@ -1883,10 +1781,7 @@ static void usb_rx_callback_intf1(struct urb *urb) break; case 0: - if (ictx->rdev->driver_type == RC_DRIVER_IR_RAW) - imon_incoming_ir_raw(ictx, urb, intfnum); - else - imon_incoming_scancode(ictx, urb, intfnum); + imon_incoming_packet(ictx, urb, intfnum); break; default: @@ -1912,7 +1807,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx) { u8 ffdc_cfg_byte = ictx->usb_rx_buf[6]; u8 detected_display_type = IMON_DISPLAY_TYPE_NONE; - u64 allowed_protos = RC_PROTO_BIT_OTHER; + u64 allowed_protos = RC_PROTO_BIT_IMON; switch (ffdc_cfg_byte) { /* iMON Knob, no display, iMON IR + vol knob */ @@ -1960,8 +1855,10 @@ static void imon_get_ffdc_type(struct imon_context *ictx) default: dev_info(ictx->dev, "Unknown 0xffdc device, defaulting to VFD and iMON IR"); detected_display_type = IMON_DISPLAY_TYPE_VFD; - /* We don't know which one it is, allow user to set the - * RC6 one from userspace if OTHER wasn't correct. */ + /* + * We don't know which one it is, allow user to set the + * RC6 one from userspace if IMON wasn't correct. + */ allowed_protos |= RC_PROTO_BIT_RC6_MCE; break; } @@ -2000,14 +1897,11 @@ static void imon_set_display_type(struct imon_context *ictx) case 0x0041: case 0x0042: case 0x0043: - case 0x8001: - case 0xff30: configured_display_type = IMON_DISPLAY_TYPE_NONE; ictx->display_supported = false; break; case 0x0036: case 0x0044: - case 0xffda: default: configured_display_type = IMON_DISPLAY_TYPE_VFD; break; @@ -2032,8 +1926,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) static const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88 }; - rdev = rc_allocate_device(ictx->dev_descr->flags & IMON_IR_RAW ? - RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE); + rdev = rc_allocate_device(RC_DRIVER_SCANCODE); if (!rdev) { dev_err(ictx->dev, "remote control dev allocation failed\n"); goto out; @@ -2051,12 +1944,8 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) rdev->dev.parent = ictx->dev; rdev->priv = ictx; - if (ictx->dev_descr->flags & IMON_IR_RAW) - rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - else - /* iMON PAD or MCE */ - rdev->allowed_protocols = RC_PROTO_BIT_OTHER | - RC_PROTO_BIT_RC6_MCE; + /* iMON PAD or MCE */ + rdev->allowed_protocols = RC_PROTO_BIT_IMON | RC_PROTO_BIT_RC6_MCE; rdev->change_protocol = imon_ir_change_protocol; rdev->driver_name = MOD_NAME; @@ -2074,8 +1963,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) imon_set_display_type(ictx); - if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE || - ictx->dev_descr->flags & IMON_IR_RAW) + if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE) rdev->map_name = RC_MAP_IMON_MCE; else rdev->map_name = RC_MAP_IMON_PAD; diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c new file mode 100644 index 000000000000..32709f96de14 --- /dev/null +++ b/drivers/media/rc/imon_raw.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2018 Sean Young <sean@mess.org> + +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/input.h> +#include <media/rc-core.h> + +/* Each bit is 250us */ +#define BIT_DURATION 250000 + +struct imon { + struct device *dev; + struct urb *ir_urb; + struct rc_dev *rcdev; + u8 ir_buf[8]; + char phys[64]; +}; + +/* + * ffs/find_next_bit() searches in the wrong direction, so open-code our own. + */ +static inline int is_bit_set(const u8 *buf, int bit) +{ + return buf[bit / 8] & (0x80 >> (bit & 7)); +} + +static void imon_ir_data(struct imon *imon) +{ + DEFINE_IR_RAW_EVENT(rawir); + int offset = 0, size = 5 * 8; + int bit; + + dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf); + + while (offset < size) { + bit = offset; + while (!is_bit_set(imon->ir_buf, bit) && bit < size) + bit++; + dev_dbg(imon->dev, "pulse: %d bits", bit - offset); + if (bit > offset) { + rawir.pulse = true; + rawir.duration = (bit - offset) * BIT_DURATION; + ir_raw_event_store_with_filter(imon->rcdev, &rawir); + } + + if (bit >= size) + break; + + offset = bit; + while (is_bit_set(imon->ir_buf, bit) && bit < size) + bit++; + dev_dbg(imon->dev, "space: %d bits", bit - offset); + + rawir.pulse = false; + rawir.duration = (bit - offset) * BIT_DURATION; + ir_raw_event_store_with_filter(imon->rcdev, &rawir); + + offset = bit; + } + + if (imon->ir_buf[7] == 0x0a) { + ir_raw_event_set_idle(imon->rcdev, true); + ir_raw_event_handle(imon->rcdev); + } +} + +static void imon_ir_rx(struct urb *urb) +{ + struct imon *imon = urb->context; + int ret; + + switch (urb->status) { + case 0: + if (imon->ir_buf[7] != 0xff) + imon_ir_data(imon); + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + case -EPIPE: + default: + dev_dbg(imon->dev, "error: urb status = %d", urb->status); + break; + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret && ret != -ENODEV) + dev_warn(imon->dev, "failed to resubmit urb: %d", ret); +} + +static int imon_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ir_ep = NULL; + struct usb_host_interface *idesc; + struct usb_device *udev; + struct rc_dev *rcdev; + struct imon *imon; + int i, ret; + + udev = interface_to_usbdev(intf); + idesc = intf->cur_altsetting; + + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(ep)) { + ir_ep = ep; + break; + } + } + + if (!ir_ep) { + dev_err(&intf->dev, "IR endpoint missing"); + return -ENODEV; + } + + imon = devm_kmalloc(&intf->dev, sizeof(*imon), GFP_KERNEL); + if (!imon) + return -ENOMEM; + + imon->ir_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!imon->ir_urb) + return -ENOMEM; + + imon->dev = &intf->dev; + usb_fill_int_urb(imon->ir_urb, udev, + usb_rcvintpipe(udev, ir_ep->bEndpointAddress), + imon->ir_buf, sizeof(imon->ir_buf), + imon_ir_rx, imon, ir_ep->bInterval); + + rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW); + if (!rcdev) { + ret = -ENOMEM; + goto free_urb; + } + + usb_make_path(udev, imon->phys, sizeof(imon->phys)); + + rcdev->device_name = "iMON Station"; + rcdev->driver_name = KBUILD_MODNAME; + rcdev->input_phys = imon->phys; + usb_to_input_id(udev, &rcdev->input_id); + rcdev->dev.parent = &intf->dev; + rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; + rcdev->map_name = RC_MAP_IMON_RSC; + rcdev->rx_resolution = BIT_DURATION; + rcdev->priv = imon; + + ret = devm_rc_register_device(&intf->dev, rcdev); + if (ret) + goto free_urb; + + imon->rcdev = rcdev; + + ret = usb_submit_urb(imon->ir_urb, GFP_KERNEL); + if (ret) + goto free_urb; + + usb_set_intfdata(intf, imon); + + return 0; + +free_urb: + usb_free_urb(imon->ir_urb); + return ret; +} + +static void imon_disconnect(struct usb_interface *intf) +{ + struct imon *imon = usb_get_intfdata(intf); + + usb_kill_urb(imon->ir_urb); + usb_free_urb(imon->ir_urb); +} + +static const struct usb_device_id imon_table[] = { + /* SoundGraph iMON (IR only) -- sg_imon.inf */ + { USB_DEVICE(0x04e8, 0xff30) }, + {} +}; + +static struct usb_driver imon_driver = { + .name = KBUILD_MODNAME, + .probe = imon_probe, + .disconnect = imon_disconnect, + .id_table = imon_table +}; + +module_usb_driver(imon_driver); + +MODULE_DESCRIPTION("Early raw iMON IR devices"); +MODULE_AUTHOR("Sean Young <sean@mess.org>"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, imon_table); diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index 0ce11c41dfae..700ab4c563d0 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -71,9 +71,10 @@ struct hix5hd2_ir_priv { unsigned long rate; }; -static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) +static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) { u32 val; + int ret = 0; if (dev->regmap) { regmap_read(dev->regmap, IR_CLK, &val); @@ -87,10 +88,11 @@ static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) regmap_write(dev->regmap, IR_CLK, val); } else { if (on) - clk_prepare_enable(dev->clock); + ret = clk_prepare_enable(dev->clock); else clk_disable_unprepare(dev->clock); } + return ret; } static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv) @@ -127,9 +129,18 @@ static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv) static int hix5hd2_ir_open(struct rc_dev *rdev) { struct hix5hd2_ir_priv *priv = rdev->priv; + int ret; + + ret = hix5hd2_ir_enable(priv, true); + if (ret) + return ret; - hix5hd2_ir_enable(priv, true); - return hix5hd2_ir_config(priv); + ret = hix5hd2_ir_config(priv); + if (ret) { + hix5hd2_ir_enable(priv, false); + return ret; + } + return 0; } static void hix5hd2_ir_close(struct rc_dev *rdev) @@ -239,7 +250,9 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) ret = PTR_ERR(priv->clock); goto err; } - clk_prepare_enable(priv->clock); + ret = clk_prepare_enable(priv->clock); + if (ret) + goto err; priv->rate = clk_get_rate(priv->clock); rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; @@ -309,9 +322,17 @@ static int hix5hd2_ir_suspend(struct device *dev) static int hix5hd2_ir_resume(struct device *dev) { struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); + int ret; - hix5hd2_ir_enable(priv, true); - clk_prepare_enable(priv->clock); + ret = hix5hd2_ir_enable(priv, true); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->clock); + if (ret) { + hix5hd2_ir_enable(priv, false); + return ret; + } writel_relaxed(0x01, priv->base + IR_ENABLE); writel_relaxed(0x00, priv->base + IR_INTM); diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c new file mode 100644 index 000000000000..a1ff06a26542 --- /dev/null +++ b/drivers/media/rc/ir-imon-decoder.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +// ir-imon-decoder.c - handle iMon protocol +// +// Copyright (C) 2018 by Sean Young <sean@mess.org> + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include "rc-core-priv.h" + +#define IMON_UNIT 415662 /* ns */ +#define IMON_BITS 30 +#define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \ + BIT(21) | BIT(20) | BIT(19) | BIT(18) | \ + BIT(17) | BIT(16) | BIT(14) | BIT(13) | \ + BIT(12) | BIT(11) | BIT(10) | BIT(9)) + +/* + * This protocol has 30 bits. The format is one IMON_UNIT header pulse, + * followed by 30 bits. Each bit is one IMON_UNIT check field, and then + * one IMON_UNIT field with the actual bit (1=space, 0=pulse). + * The check field is always space for some bits, for others it is pulse if + * both the preceding and current bit are zero, else space. IMON_CHKBITS + * defines which bits are of type check. + * + * There is no way to distinguish an incomplete message from one where + * the lower bits are all set, iow. the last pulse is for the lowest + * bit which is 0. + */ +enum imon_state { + STATE_INACTIVE, + STATE_BIT_CHK, + STATE_BIT_START, + STATE_FINISHED +}; + +/** + * ir_imon_decode() - Decode one iMON pulse or space + * @dev: the struct rc_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct imon_dec *data = &dev->raw->imon; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + dev_dbg(&dev->dev, + "iMON decode started at state %d bitno %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), + TO_STR(ev.pulse)); + + for (;;) { + if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2)) + return 0; + + decrease_duration(&ev, IMON_UNIT); + + switch (data->state) { + case STATE_INACTIVE: + if (ev.pulse) { + data->state = STATE_BIT_CHK; + data->bits = 0; + data->count = IMON_BITS; + } + break; + case STATE_BIT_CHK: + if (IMON_CHKBITS & BIT(data->count)) + data->last_chk = ev.pulse; + else if (ev.pulse) + goto err_out; + data->state = STATE_BIT_START; + break; + case STATE_BIT_START: + data->bits <<= 1; + if (!ev.pulse) + data->bits |= 1; + + if (IMON_CHKBITS & BIT(data->count)) { + if (data->last_chk != !(data->bits & 3)) + goto err_out; + } + + if (!data->count--) + data->state = STATE_FINISHED; + else + data->state = STATE_BIT_CHK; + break; + case STATE_FINISHED: + if (ev.pulse) + goto err_out; + rc_keydown(dev, RC_PROTO_IMON, data->bits, 0); + data->state = STATE_INACTIVE; + break; + } + } + +err_out: + dev_dbg(&dev->dev, + "iMON decode failed at state %d bitno %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), + TO_STR(ev.pulse)); + + data->state = STATE_INACTIVE; + + return -EINVAL; +} + +/** + * ir_imon_encode() - Encode a scancode as a stream of raw events + * + * @protocol: protocol to encode + * @scancode: scancode to encode + * @events: array of raw ir events to write into + * @max: maximum size of @events + * + * Returns: The number of events written. + * -ENOBUFS if there isn't enough space in the array to fit the + * encoding. In this case all @max events will have been written. + */ +static int ir_imon_encode(enum rc_proto protocol, u32 scancode, + struct ir_raw_event *events, unsigned int max) +{ + struct ir_raw_event *e = events; + int i, pulse; + + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(e, 1, IMON_UNIT); + + for (i = IMON_BITS; i >= 0; i--) { + if (BIT(i) & IMON_CHKBITS) + pulse = !(scancode & (BIT(i) | BIT(i + 1))); + else + pulse = 0; + + if (pulse == e->pulse) { + e->duration += IMON_UNIT; + } else { + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(++e, pulse, IMON_UNIT); + } + + pulse = !(scancode & BIT(i)); + + if (pulse == e->pulse) { + e->duration += IMON_UNIT; + } else { + if (!max--) + return -ENOBUFS; + init_ir_raw_event_duration(++e, pulse, IMON_UNIT); + } + } + + if (e->pulse) + e++; + + return e - events; +} + +static struct ir_raw_handler imon_handler = { + .protocols = RC_PROTO_BIT_IMON, + .decode = ir_imon_decode, + .encode = ir_imon_encode, + .carrier = 38000, +}; + +static int __init ir_imon_decode_init(void) +{ + ir_raw_handler_register(&imon_handler); + + pr_info("IR iMON protocol handler initialized\n"); + return 0; +} + +static void __exit ir_imon_decode_exit(void) +{ + ir_raw_handler_unregister(&imon_handler); +} + +module_init(ir_imon_decode_init); +module_exit(ir_imon_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young <sean@mess.org>"); +MODULE_DESCRIPTION("iMON IR protocol decoder"); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index c03c776cfa54..8cb68ae43282 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -56,8 +56,8 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2)) goto out; - IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "JVC decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); again: switch (data->state) { @@ -136,15 +136,15 @@ again: u32 scancode; scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) | (bitrev8((data->bits >> 0) & 0xff) << 0); - IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); + dev_dbg(&dev->dev, "JVC scancode 0x%04x\n", scancode); rc_keydown(dev, RC_PROTO_JVC, scancode, data->toggle); data->first = false; data->old_bits = data->bits; } else if (data->bits == data->old_bits) { - IR_dprintk(1, "JVC repeat\n"); + dev_dbg(&dev->dev, "JVC repeat\n"); rc_repeat(dev); } else { - IR_dprintk(1, "JVC invalid repeat msg\n"); + dev_dbg(&dev->dev, "JVC invalid repeat msg\n"); break; } @@ -164,8 +164,8 @@ again: } out: - IR_dprintk(1, "JVC decode failed at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "JVC decode failed at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index 2c3df02e05ff..c110984ca671 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -117,19 +117,19 @@ static unsigned char kbd_keycodes[256] = { static void mce_kbd_rx_timeout(struct timer_list *t) { - struct mce_kbd_dec *mce_kbd = from_timer(mce_kbd, t, rx_timeout); - int i; + struct ir_raw_event_ctrl *raw = from_timer(raw, t, mce_kbd.rx_timeout); unsigned char maskcode; + int i; - IR_dprintk(2, "timer callback clearing all keys\n"); + dev_dbg(&raw->dev->dev, "timer callback clearing all keys\n"); for (i = 0; i < 7; i++) { maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; - input_report_key(mce_kbd->idev, maskcode, 0); + input_report_key(raw->mce_kbd.idev, maskcode, 0); } for (i = 0; i < MCIR2_MASK_KEYS_START; i++) - input_report_key(mce_kbd->idev, kbd_keycodes[i], 0); + input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0); } static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data) @@ -144,16 +144,16 @@ static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data) } } -static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev, - u32 scancode) +static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode) { + struct mce_kbd_dec *data = &dev->raw->mce_kbd; u8 keydata = (scancode >> 8) & 0xff; u8 shiftmask = scancode & 0xff; unsigned char keycode, maskcode; int i, keystate; - IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n", - keydata, shiftmask); + dev_dbg(&dev->dev, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n", + keydata, shiftmask); for (i = 0; i < 7; i++) { maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; @@ -161,20 +161,21 @@ static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev, keystate = 1; else keystate = 0; - input_report_key(idev, maskcode, keystate); + input_report_key(data->idev, maskcode, keystate); } if (keydata) { keycode = kbd_keycodes[keydata]; - input_report_key(idev, keycode, 1); + input_report_key(data->idev, keycode, 1); } else { for (i = 0; i < MCIR2_MASK_KEYS_START; i++) - input_report_key(idev, kbd_keycodes[i], 0); + input_report_key(data->idev, kbd_keycodes[i], 0); } } -static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode) +static void ir_mce_kbd_process_mouse_data(struct rc_dev *dev, u32 scancode) { + struct mce_kbd_dec *data = &dev->raw->mce_kbd; /* raw mouse coordinates */ u8 xdata = (scancode >> 7) & 0x7f; u8 ydata = (scancode >> 14) & 0x7f; @@ -193,14 +194,14 @@ static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode) else y = ydata; - IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n", - x, y, left ? "L" : "", right ? "R" : ""); + dev_dbg(&dev->dev, "mouse: x = %d, y = %d, btns = %s%s\n", + x, y, left ? "L" : "", right ? "R" : ""); - input_report_rel(idev, REL_X, x); - input_report_rel(idev, REL_Y, y); + input_report_rel(data->idev, REL_X, x); + input_report_rel(data->idev, REL_Y, y); - input_report_key(idev, BTN_LEFT, left); - input_report_key(idev, BTN_RIGHT, right); + input_report_key(data->idev, BTN_LEFT, left); + input_report_key(data->idev, BTN_RIGHT, right); } /** @@ -227,8 +228,8 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; again: - IR_dprintk(2, "started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2)) return 0; @@ -280,7 +281,7 @@ again: data->wanted_bits = MCIR2_MOUSE_NBITS; break; default: - IR_dprintk(1, "not keyboard or mouse data\n"); + dev_dbg(&dev->dev, "not keyboard or mouse data\n"); goto out; } @@ -319,25 +320,26 @@ again: switch (data->wanted_bits) { case MCIR2_KEYBOARD_NBITS: scancode = data->body & 0xffff; - IR_dprintk(1, "keyboard data 0x%08x\n", data->body); + dev_dbg(&dev->dev, "keyboard data 0x%08x\n", + data->body); if (dev->timeout) delay = usecs_to_jiffies(dev->timeout / 1000); else delay = msecs_to_jiffies(100); mod_timer(&data->rx_timeout, jiffies + delay); /* Pass data to keyboard buffer parser */ - ir_mce_kbd_process_keyboard_data(data->idev, scancode); + ir_mce_kbd_process_keyboard_data(dev, scancode); lsc.rc_proto = RC_PROTO_MCIR2_KBD; break; case MCIR2_MOUSE_NBITS: scancode = data->body & 0x1fffff; - IR_dprintk(1, "mouse data 0x%06x\n", scancode); + dev_dbg(&dev->dev, "mouse data 0x%06x\n", scancode); /* Pass data to mouse buffer parser */ - ir_mce_kbd_process_mouse_data(data->idev, scancode); + ir_mce_kbd_process_mouse_data(dev, scancode); lsc.rc_proto = RC_PROTO_MCIR2_MSE; break; default: - IR_dprintk(1, "not keyboard or mouse data\n"); + dev_dbg(&dev->dev, "not keyboard or mouse data\n"); goto out; } @@ -350,8 +352,8 @@ again: } out: - IR_dprintk(1, "failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; input_sync(data->idev); return -EINVAL; diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 31d7bafe7bda..21647b809e6f 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -49,8 +49,8 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; } - IR_dprintk(2, "NEC decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "NEC decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); switch (data->state) { @@ -99,13 +99,11 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) break; if (data->necx_repeat && data->count == NECX_REPEAT_BITS && - geq_margin(ev.duration, - NEC_TRAILER_SPACE, NEC_UNIT / 2)) { - IR_dprintk(1, "Repeat last key\n"); - rc_repeat(dev); - data->state = STATE_INACTIVE; - return 0; - + geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) { + dev_dbg(&dev->dev, "Repeat last key\n"); + rc_repeat(dev); + data->state = STATE_INACTIVE; + return 0; } else if (data->count > NECX_REPEAT_BITS) data->necx_repeat = false; @@ -164,8 +162,8 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; } - IR_dprintk(1, "NEC decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "NEC decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 11a28f8772da..74d3b859c3a2 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -54,8 +54,8 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; again: - IR_dprintk(2, "RC5(x/sz) decode started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "RC5(x/sz) decode started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) return 0; @@ -157,8 +157,8 @@ again: } else break; - IR_dprintk(1, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n", - scancode, protocol, toggle); + dev_dbg(&dev->dev, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n", + scancode, protocol, toggle); rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; @@ -166,8 +166,8 @@ again: } out: - IR_dprintk(1, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 55bb19bbd4e9..8314da32453f 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -100,8 +100,8 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; again: - IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "RC6 decode started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) return 0; @@ -170,7 +170,7 @@ again: break; if (!(data->header & RC6_STARTBIT_MASK)) { - IR_dprintk(1, "RC6 invalid start bit\n"); + dev_dbg(&dev->dev, "RC6 invalid start bit\n"); break; } @@ -187,7 +187,7 @@ again: data->wanted_bits = RC6_6A_NBITS; break; default: - IR_dprintk(1, "RC6 unknown mode\n"); + dev_dbg(&dev->dev, "RC6 unknown mode\n"); goto out; } goto again; @@ -230,13 +230,13 @@ again: scancode = data->body; toggle = data->toggle; protocol = RC_PROTO_RC6_0; - IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", - scancode, toggle); + dev_dbg(&dev->dev, "RC6(0) scancode 0x%04x (toggle: %u)\n", + scancode, toggle); break; case RC6_MODE_6A: if (data->count > CHAR_BIT * sizeof data->body) { - IR_dprintk(1, "RC6 too many (%u) data bits\n", + dev_dbg(&dev->dev, "RC6 too many (%u) data bits\n", data->count); goto out; } @@ -262,15 +262,15 @@ again: } break; default: - IR_dprintk(1, "RC6(6A) unsupported length\n"); + dev_dbg(&dev->dev, "RC6(6A) unsupported length\n"); goto out; } - IR_dprintk(1, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n", - protocol, scancode, toggle); + dev_dbg(&dev->dev, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n", + protocol, scancode, toggle); break; default: - IR_dprintk(1, "RC6 unknown mode\n"); + dev_dbg(&dev->dev, "RC6 unknown mode\n"); goto out; } @@ -280,8 +280,8 @@ again: } out: - IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "RC6 decode failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index ded39cdfc6ef..4efe6db5376a 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -52,14 +52,14 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!is_timing_event(ev)) { if (ev.reset) { - IR_dprintk(1, "SANYO event reset received. reset to state 0\n"); + dev_dbg(&dev->dev, "SANYO event reset received. reset to state 0\n"); data->state = STATE_INACTIVE; } return 0; } - IR_dprintk(2, "SANYO decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "SANYO decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); switch (data->state) { @@ -102,7 +102,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) { rc_repeat(dev); - IR_dprintk(1, "SANYO repeat last key\n"); + dev_dbg(&dev->dev, "SANYO repeat last key\n"); data->state = STATE_INACTIVE; return 0; } @@ -144,21 +144,21 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) not_command = bitrev8((data->bits >> 0) & 0xff); if ((command ^ not_command) != 0xff) { - IR_dprintk(1, "SANYO checksum error: received 0x%08Lx\n", - data->bits); + dev_dbg(&dev->dev, "SANYO checksum error: received 0x%08llx\n", + data->bits); data->state = STATE_INACTIVE; return 0; } scancode = address << 8 | command; - IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode); + dev_dbg(&dev->dev, "SANYO scancode: 0x%06x\n", scancode); rc_keydown(dev, RC_PROTO_SANYO, scancode, 0); data->state = STATE_INACTIVE; return 0; } - IR_dprintk(1, "SANYO decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "SANYO decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index df296991906c..6a38c50566a4 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -54,8 +54,8 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; } - IR_dprintk(2, "Sharp decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "Sharp decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); switch (data->state) { @@ -149,9 +149,9 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) msg = (data->bits >> 15) & 0x7fff; echo = data->bits & 0x7fff; if ((msg ^ echo) != 0x3ff) { - IR_dprintk(1, - "Sharp checksum error: received 0x%04x, 0x%04x\n", - msg, echo); + dev_dbg(&dev->dev, + "Sharp checksum error: received 0x%04x, 0x%04x\n", + msg, echo); break; } @@ -159,16 +159,15 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) command = bitrev8((msg >> 2) & 0xff); scancode = address << 8 | command; - IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode); + dev_dbg(&dev->dev, "Sharp scancode 0x%04x\n", scancode); rc_keydown(dev, RC_PROTO_SHARP, scancode, 0); data->state = STATE_INACTIVE; return 0; } - IR_dprintk(1, "Sharp decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), - TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "Sharp decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index e4bcff21c025..6764ec9de646 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -55,8 +55,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) goto out; - IR_dprintk(2, "Sony decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "Sony decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); switch (data->state) { @@ -148,19 +148,21 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) protocol = RC_PROTO_SONY20; break; default: - IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); + dev_dbg(&dev->dev, "Sony invalid bitcount %u\n", + data->count); goto out; } scancode = device << 16 | subdevice << 8 | function; - IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode); + dev_dbg(&dev->dev, "Sony(%u) scancode 0x%05x\n", data->count, + scancode); rc_keydown(dev, protocol, scancode, 0); goto finish_state_machine; } out: - IR_dprintk(1, "Sony decode failed at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "Sony decode failed at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index a32a84ae2d0b..7163d5ce2e64 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -15,21 +15,11 @@ #define IR_SPI_DRIVER_NAME "ir-spi" -/* pulse value for different duty cycles */ -#define IR_SPI_PULSE_DC_50 0xff00 -#define IR_SPI_PULSE_DC_60 0xfc00 -#define IR_SPI_PULSE_DC_70 0xf800 -#define IR_SPI_PULSE_DC_75 0xf000 -#define IR_SPI_PULSE_DC_80 0xc000 -#define IR_SPI_PULSE_DC_90 0x8000 - #define IR_SPI_DEFAULT_FREQUENCY 38000 -#define IR_SPI_BIT_PER_WORD 8 #define IR_SPI_MAX_BUFSIZE 4096 struct ir_spi_data { u32 freq; - u8 duty_cycle; bool negated; u16 tx_buf[IR_SPI_MAX_BUFSIZE]; @@ -105,19 +95,9 @@ static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier) static int ir_spi_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle) { struct ir_spi_data *idata = dev->priv; + int bits = (duty_cycle * 15) / 100; - if (duty_cycle >= 90) - idata->pulse = IR_SPI_PULSE_DC_90; - else if (duty_cycle >= 80) - idata->pulse = IR_SPI_PULSE_DC_80; - else if (duty_cycle >= 75) - idata->pulse = IR_SPI_PULSE_DC_75; - else if (duty_cycle >= 70) - idata->pulse = IR_SPI_PULSE_DC_70; - else if (duty_cycle >= 60) - idata->pulse = IR_SPI_PULSE_DC_60; - else - idata->pulse = IR_SPI_PULSE_DC_50; + idata->pulse = GENMASK(bits, 0); if (idata->negated) { idata->pulse = ~idata->pulse; diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c index 712bc6d76e92..58b47af1a763 100644 --- a/drivers/media/rc/ir-xmp-decoder.c +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -49,8 +49,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; } - IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "XMP decode started at state %d %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); switch (data->state) { @@ -85,7 +85,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; if (data->count != 16) { - IR_dprintk(2, "received TRAILER period at index %d: %u\n", + dev_dbg(&dev->dev, "received TRAILER period at index %d: %u\n", data->count, ev.duration); data->state = STATE_INACTIVE; return -EINVAL; @@ -99,7 +99,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) */ divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000; if (divider < 50) { - IR_dprintk(2, "divider to small %d.\n", divider); + dev_dbg(&dev->dev, "divider to small %d.\n", + divider); data->state = STATE_INACTIVE; return -EINVAL; } @@ -113,7 +114,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) n[12] + n[13] + n[14] + n[15]) % 16; if (sum1 != 15 || sum2 != 15) { - IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n", + dev_dbg(&dev->dev, "checksum errors sum1=0x%X sum2=0x%X\n", sum1, sum2); data->state = STATE_INACTIVE; return -EINVAL; @@ -127,24 +128,24 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) obc1 = n[12] << 4 | n[13]; obc2 = n[14] << 4 | n[15]; if (subaddr != subaddr2) { - IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n", + dev_dbg(&dev->dev, "subaddress nibbles mismatch 0x%02X != 0x%02X\n", subaddr, subaddr2); data->state = STATE_INACTIVE; return -EINVAL; } if (oem != 0x44) - IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n", + dev_dbg(&dev->dev, "Warning: OEM nibbles 0x%02X. Expected 0x44\n", oem); scancode = addr << 24 | subaddr << 16 | obc1 << 8 | obc2; - IR_dprintk(1, "XMP scancode 0x%06x\n", scancode); + dev_dbg(&dev->dev, "XMP scancode 0x%06x\n", scancode); if (toggle == 0) { rc_keydown(dev, RC_PROTO_XMP, scancode, 0); } else { rc_repeat(dev); - IR_dprintk(1, "Repeat last key\n"); + dev_dbg(&dev->dev, "Repeat last key\n"); } data->state = STATE_INACTIVE; @@ -153,7 +154,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) { /* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */ if (data->count == 16) { - IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n", + dev_dbg(&dev->dev, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n", data->count, ev.duration); /* * TODO: for now go back to half frame position @@ -164,7 +165,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } else if (data->count != 8) - IR_dprintk(2, "received half frame pulse at index %d: %u\n", + dev_dbg(&dev->dev, "received half frame pulse at index %d: %u\n", data->count, ev.duration); data->state = STATE_LEADER_PULSE; @@ -173,7 +174,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) { /* store nibble raw data, decode after trailer */ if (data->count == 16) { - IR_dprintk(2, "to many pulses (%d) ignoring: %u\n", + dev_dbg(&dev->dev, "to many pulses (%d) ignoring: %u\n", data->count, ev.duration); data->state = STATE_INACTIVE; return -EINVAL; @@ -189,8 +190,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) break; } - IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "XMP decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 50b319355edf..d6b913a3032d 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-hisi-tv-demo.o \ rc-imon-mce.o \ rc-imon-pad.o \ + rc-imon-rsc.o \ rc-iodata-bctv7e.o \ rc-it913x-v1.o \ rc-it913x-v2.o \ diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c index a7296ffbf218..8501cf0a3253 100644 --- a/drivers/media/rc/keymaps/rc-imon-pad.c +++ b/drivers/media/rc/keymaps/rc-imon-pad.c @@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = { .map = { .scan = imon_pad, .size = ARRAY_SIZE(imon_pad), - /* actual protocol details unknown, hardware decoder */ - .rc_proto = RC_PROTO_OTHER, + .rc_proto = RC_PROTO_IMON, .name = RC_MAP_IMON_PAD, } }; diff --git a/drivers/media/rc/keymaps/rc-imon-rsc.c b/drivers/media/rc/keymaps/rc-imon-rsc.c new file mode 100644 index 000000000000..83e4564aaa22 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-imon-rsc.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2018 Sean Young <sean@mess.org> + +#include <media/rc-map.h> +#include <linux/module.h> + +// +// Note that this remote has a stick which its own IR protocol, +// with 16 directions. This is not supported yet. +// +static struct rc_map_table imon_rsc[] = { + { 0x801010, KEY_EXIT }, + { 0x80102f, KEY_POWER }, + { 0x80104a, KEY_SCREENSAVER }, /* Screensaver */ + { 0x801049, KEY_TIME }, /* Timer */ + { 0x801054, KEY_NUMERIC_1 }, + { 0x801055, KEY_NUMERIC_2 }, + { 0x801056, KEY_NUMERIC_3 }, + { 0x801057, KEY_NUMERIC_4 }, + { 0x801058, KEY_NUMERIC_5 }, + { 0x801059, KEY_NUMERIC_6 }, + { 0x80105a, KEY_NUMERIC_7 }, + { 0x80105b, KEY_NUMERIC_8 }, + { 0x80105c, KEY_NUMERIC_9 }, + { 0x801081, KEY_SCREEN }, /* Desktop */ + { 0x80105d, KEY_NUMERIC_0 }, + { 0x801082, KEY_MAX }, + { 0x801048, KEY_ESC }, + { 0x80104b, KEY_MEDIA }, /* Windows key */ + { 0x801083, KEY_MENU }, + { 0x801045, KEY_APPSELECT }, /* app launcher */ + { 0x801084, KEY_STOP }, + { 0x801046, KEY_CYCLEWINDOWS }, + { 0x801085, KEY_BACKSPACE }, + { 0x801086, KEY_KEYBOARD }, + { 0x801087, KEY_SPACE }, + { 0x80101e, KEY_RESERVED }, /* shift tab */ + { 0x801098, BTN_0 }, + { 0x80101f, KEY_TAB }, + { 0x80101b, BTN_LEFT }, + { 0x80101d, BTN_RIGHT }, + { 0x801016, BTN_MIDDLE }, /* drag and drop */ + { 0x801088, KEY_MUTE }, + { 0x80105e, KEY_VOLUMEDOWN }, + { 0x80105f, KEY_VOLUMEUP }, + { 0x80104c, KEY_PLAY }, + { 0x80104d, KEY_PAUSE }, + { 0x80104f, KEY_EJECTCD }, + { 0x801050, KEY_PREVIOUS }, + { 0x801051, KEY_NEXT }, + { 0x80104e, KEY_STOP }, + { 0x801052, KEY_REWIND }, + { 0x801053, KEY_FASTFORWARD }, + { 0x801089, KEY_ZOOM } /* full screen */ +}; + +static struct rc_map_list imon_rsc_map = { + .map = { + .scan = imon_rsc, + .size = ARRAY_SIZE(imon_rsc), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_IMON_RSC, + } +}; + +static int __init init_rc_map_imon_rsc(void) +{ + return rc_map_register(&imon_rsc_map); +} + +static void __exit exit_rc_map_imon_rsc(void) +{ + rc_map_unregister(&imon_rsc_map); +} + +module_init(init_rc_map_imon_rsc) +module_exit(exit_rc_map_imon_rsc) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young <sean@mess.org>"); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index cc863044c880..24e9fbb80e81 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -60,12 +60,12 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) * space with the maximum time value. */ sample = LIRC_SPACE(LIRC_VALUE_MASK); - IR_dprintk(2, "delivering reset sync space to lirc_dev\n"); + dev_dbg(&dev->dev, "delivering reset sync space to lirc_dev\n"); /* Carrier reports */ } else if (ev.carrier_report) { sample = LIRC_FREQUENCY(ev.carrier); - IR_dprintk(2, "carrier report (freq: %d)\n", sample); + dev_dbg(&dev->dev, "carrier report (freq: %d)\n", sample); /* Packet end */ } else if (ev.timeout) { @@ -77,7 +77,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) dev->gap_duration = ev.duration; sample = LIRC_TIMEOUT(ev.duration / 1000); - IR_dprintk(2, "timeout report (duration: %d)\n", sample); + dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample); /* Normal sample */ } else { @@ -100,8 +100,8 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) : LIRC_SPACE(ev.duration / 1000); - IR_dprintk(2, "delivering %uus %s to lirc_dev\n", - TO_US(ev.duration), TO_STR(ev.pulse)); + dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n", + TO_US(ev.duration), TO_STR(ev.pulse)); } spin_lock_irqsave(&dev->lirc_fh_lock, flags); @@ -249,8 +249,6 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, goto out_unlock; } - start = ktime_get(); - if (!dev->tx_ir) { ret = -EINVAL; goto out_unlock; @@ -343,6 +341,8 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, duration += txbuf[i]; } + start = ktime_get(); + ret = dev->tx_ir(dev, txbuf, count); if (ret < 0) goto out_kfree; @@ -570,7 +570,7 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd, ret = -EINVAL; else if (dev->s_timeout) ret = dev->s_timeout(dev, tmp); - else if (!ret) + else dev->timeout = tmp; } break; @@ -804,8 +804,8 @@ int __init lirc_dev_init(void) return retval; } - pr_info("IR Remote Control driver registered, major %d\n", - MAJOR(lirc_base_dev)); + pr_debug("IR Remote Control driver registered, major %d\n", + MAJOR(lirc_base_dev)); return 0; } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index a9187b0b46a1..69ba57372c05 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -42,7 +42,7 @@ #include <linux/pm_wakeup.h> #include <media/rc-core.h> -#define DRIVER_VERSION "1.93" +#define DRIVER_VERSION "1.94" #define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" #define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ "device driver" @@ -182,6 +182,7 @@ enum mceusb_model_type { MCE_GEN1, MCE_GEN3, MCE_GEN2_TX_INV, + MCE_GEN2_TX_INV_RX_GOOD, POLARIS_EVK, CX_HYBRID_TV, MULTIFUNCTION, @@ -198,6 +199,13 @@ struct mceusb_model { u32 mce_gen3:1; u32 tx_mask_normal:1; u32 no_tx:1; + /* + * 2nd IR receiver (short-range, wideband) for learning mode: + * 0, absent 2nd receiver (rx2) + * 1, rx2 present + * 2, rx2 which under counts IR carrier cycles + */ + u32 rx2; int ir_intfnum; @@ -209,9 +217,11 @@ static const struct mceusb_model mceusb_model[] = { [MCE_GEN1] = { .mce_gen1 = 1, .tx_mask_normal = 1, + .rx2 = 2, }, [MCE_GEN2] = { .mce_gen2 = 1, + .rx2 = 2, }, [MCE_GEN2_NO_TX] = { .mce_gen2 = 1, @@ -220,10 +230,17 @@ static const struct mceusb_model mceusb_model[] = { [MCE_GEN2_TX_INV] = { .mce_gen2 = 1, .tx_mask_normal = 1, + .rx2 = 1, + }, + [MCE_GEN2_TX_INV_RX_GOOD] = { + .mce_gen2 = 1, + .tx_mask_normal = 1, + .rx2 = 2, }, [MCE_GEN3] = { .mce_gen3 = 1, .tx_mask_normal = 1, + .rx2 = 2, }, [POLARIS_EVK] = { /* @@ -232,6 +249,7 @@ static const struct mceusb_model mceusb_model[] = { * to allow testing it */ .name = "Conexant Hybrid TV (cx231xx) MCE IR", + .rx2 = 2, }, [CX_HYBRID_TV] = { .no_tx = 1, /* tx isn't wired up at all */ @@ -244,10 +262,12 @@ static const struct mceusb_model mceusb_model[] = { [MULTIFUNCTION] = { .mce_gen2 = 1, .ir_intfnum = 2, + .rx2 = 2, }, [TIVO_KIT] = { .mce_gen2 = 1, .rc_map = RC_MAP_TIVO, + .rx2 = 2, }, [EVROMEDIA_FULL_HYBRID_FULLHD] = { .name = "Evromedia USB Full Hybrid Full HD", @@ -290,7 +310,7 @@ static const struct usb_device_id mceusb_dev_table[] = { .driver_info = MULTIFUNCTION }, /* SMK/Toshiba G83C0004D410 */ { USB_DEVICE(VENDOR_SMK, 0x031d), - .driver_info = MCE_GEN2_TX_INV }, + .driver_info = MCE_GEN2_TX_INV_RX_GOOD }, /* SMK eHome Infrared Transceiver (Sony VAIO) */ { USB_DEVICE(VENDOR_SMK, 0x0322), .driver_info = MCE_GEN2_TX_INV }, @@ -427,7 +447,8 @@ struct mceusb_dev { struct rc_dev *rc; /* optional features we can enable */ - bool learning_enabled; + bool carrier_report_enabled; + bool wideband_rx_enabled; /* aka learning mode, short-range rx */ /* core device bits */ struct device *dev; @@ -458,6 +479,7 @@ struct mceusb_dev { u32 tx_mask_normal:1; u32 microsoft_gen1:1; u32 no_tx:1; + u32 rx2; } flags; /* transmit support */ @@ -474,6 +496,11 @@ struct mceusb_dev { u8 num_rxports; /* number of receive sensors */ u8 txports_cabled; /* bitmask of transmitters with cable */ u8 rxports_active; /* bitmask of active receive sensors */ + bool learning_active; /* wideband rx is active */ + + /* receiver carrier frequency detection support */ + u32 pulse_tunit; /* IR pulse "on" cumulative time units */ + u32 pulse_count; /* pulse "on" count in measurement interval */ /* * support for async error handler mceusb_deferred_kevent() @@ -684,8 +711,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, /* aka MCE_RSP_EQIRRXCFCNT */ if (out) dev_dbg(dev, "Get receive sensor"); - else if (ir->learning_enabled) - dev_dbg(dev, "RX pulse count: %d", + else + dev_dbg(dev, "RX carrier cycle count: %d", ((data[0] << 8) | data[1])); break; case MCE_RSP_EQIRNUMPORTS: @@ -956,14 +983,78 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier) } /* + * Select or deselect the 2nd receiver port. + * Second receiver is learning mode, wide-band, short-range receiver. + * Only one receiver (long or short range) may be active at a time. + */ +static int mceusb_set_rx_wideband(struct rc_dev *dev, int enable) +{ + struct mceusb_dev *ir = dev->priv; + unsigned char cmdbuf[3] = { MCE_CMD_PORT_IR, + MCE_CMD_SETIRRXPORTEN, 0x00 }; + + dev_dbg(ir->dev, "select %s-range receive sensor", + enable ? "short" : "long"); + if (enable) { + ir->wideband_rx_enabled = true; + cmdbuf[2] = 2; /* port 2 is short range receiver */ + } else { + ir->wideband_rx_enabled = false; + cmdbuf[2] = 1; /* port 1 is long range receiver */ + } + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + /* response from device sets ir->learning_active */ + + return 0; +} + +/* + * Enable/disable receiver carrier frequency pass through reporting. + * Only the short-range receiver has carrier frequency measuring capability. + * Implicitly select this receiver when enabling carrier frequency reporting. + */ +static int mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable) +{ + struct mceusb_dev *ir = dev->priv; + unsigned char cmdbuf[3] = { MCE_CMD_PORT_IR, + MCE_CMD_SETIRRXPORTEN, 0x00 }; + + dev_dbg(ir->dev, "%s short-range receiver carrier reporting", + enable ? "enable" : "disable"); + if (enable) { + ir->carrier_report_enabled = true; + if (!ir->learning_active) { + cmdbuf[2] = 2; /* port 2 is short range receiver */ + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + } + } else { + ir->carrier_report_enabled = false; + /* + * Revert to normal (long-range) receiver only if the + * wideband (short-range) receiver wasn't explicitly + * enabled. + */ + if (ir->learning_active && !ir->wideband_rx_enabled) { + cmdbuf[2] = 1; /* port 1 is long range receiver */ + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + } + } + + return 0; +} + +/* * We don't do anything but print debug spew for many of the command bits * we receive from the hardware, but some of them are useful information * we want to store so that we can use them. */ static void mceusb_handle_command(struct mceusb_dev *ir, int index) { + DEFINE_IR_RAW_EVENT(rawir); u8 hi = ir->buf_in[index + 1] & 0xff; u8 lo = ir->buf_in[index + 2] & 0xff; + u32 carrier_cycles; + u32 cycles_fix; switch (ir->buf_in[index]) { /* the one and only 5-byte return value command */ @@ -980,6 +1071,33 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) ir->num_txports = hi; ir->num_rxports = lo; break; + case MCE_RSP_EQIRRXCFCNT: + /* + * The carrier cycle counter can overflow and wrap around + * without notice from the device. So frequency measurement + * will be inaccurate with long duration IR. + * + * The long-range (non learning) receiver always reports + * zero count so we always ignore its report. + */ + if (ir->carrier_report_enabled && ir->learning_active && + ir->pulse_tunit > 0) { + carrier_cycles = (hi << 8 | lo); + /* + * Adjust carrier cycle count by adding + * 1 missed count per pulse "on" + */ + cycles_fix = ir->flags.rx2 == 2 ? ir->pulse_count : 0; + rawir.carrier_report = 1; + rawir.carrier = (1000000u / MCE_TIME_UNIT) * + (carrier_cycles + cycles_fix) / + ir->pulse_tunit; + dev_dbg(ir->dev, "RX carrier frequency %u Hz (pulse count = %u, cycles = %u, duration = %u, rx2 = %u)", + rawir.carrier, ir->pulse_count, carrier_cycles, + ir->pulse_tunit, ir->flags.rx2); + ir_raw_event_store(ir->rc, &rawir); + } + break; /* 1-byte return value commands */ case MCE_RSP_EQEMVER: @@ -989,8 +1107,12 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) ir->tx_mask = hi; break; case MCE_RSP_EQIRRXPORTEN: - ir->learning_enabled = ((hi & 0x02) == 0x02); - ir->rxports_active = hi; + ir->learning_active = ((hi & 0x02) == 0x02); + if (ir->rxports_active != hi) { + dev_info(ir->dev, "%s-range (0x%x) receiver active", + ir->learning_active ? "short" : "long", hi); + ir->rxports_active = hi; + } break; case MCE_RSP_CMD_ILLEGAL: ir->need_reset = true; @@ -1027,12 +1149,16 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->rem--; init_ir_raw_event(&rawir); rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); - rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) - * US_TO_NS(MCE_TIME_UNIT); + rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK); + if (rawir.pulse) { + ir->pulse_tunit += rawir.duration; + ir->pulse_count++; + } + rawir.duration *= US_TO_NS(MCE_TIME_UNIT); - dev_dbg(ir->dev, "Storing %s with duration %u", + dev_dbg(ir->dev, "Storing %s %u ns (%02x)", rawir.pulse ? "pulse" : "space", - rawir.duration); + rawir.duration, ir->buf_in[i]); if (ir_raw_event_store_with_filter(ir->rc, &rawir)) event = true; @@ -1053,10 +1179,13 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); mceusb_dev_printdata(ir, ir->buf_in, buf_len, i, ir->rem + 1, false); - if (ir->rem) + if (ir->rem) { ir->parser_state = PARSE_IRDATA; - else + } else { ir_raw_event_reset(ir->rc); + ir->pulse_tunit = 0; + ir->pulse_count = 0; + } break; } @@ -1292,6 +1421,10 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->s_tx_carrier = mceusb_set_tx_carrier; rc->tx_ir = mceusb_tx_ir; } + if (ir->flags.rx2 > 0) { + rc->s_learning_mode = mceusb_set_rx_wideband; + rc->s_carrier_report = mceusb_set_rx_carrier_report; + } rc->driver_name = DRIVER_NAME; switch (le16_to_cpu(udev->descriptor.idVendor)) { @@ -1406,6 +1539,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, ir->flags.microsoft_gen1 = is_microsoft_gen1; ir->flags.tx_mask_normal = tx_mask_normal; ir->flags.no_tx = mceusb_model[model].no_tx; + ir->flags.rx2 = mceusb_model[model].rx2; ir->model = model; /* Saving usb interface data for use by the transmitter routine */ diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index f2204eb77e2a..f449b35d25e7 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -97,8 +97,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) status = readl_relaxed(ir->reg + IR_DEC_STATUS); rawir.pulse = !!(status & STATUS_IR_DEC_IN); - ir_raw_event_store(ir->rc, &rawir); - ir_raw_event_handle(ir->rc); + ir_raw_event_store_with_timeout(ir->rc, &rawir); spin_unlock(&ir->lock); @@ -145,7 +144,9 @@ static int meson_ir_probe(struct platform_device *pdev) ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; ir->rc->rx_resolution = US_TO_NS(MESON_TRATE); - ir->rc->timeout = MS_TO_NS(200); + ir->rc->min_timeout = 1; + ir->rc->timeout = IR_DEFAULT_TIMEOUT; + ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; ir->rc->driver_name = DRIVER_NAME; spin_lock_init(&ir->lock); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 458e9eb2d6a9..e0e6a17460f6 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -50,8 +50,9 @@ struct ir_raw_event_ctrl { DECLARE_KFIFO(kfifo, struct ir_raw_event, MAX_IR_EVENT_SIZE); ktime_t last_event; /* when last event occurred */ struct rc_dev *dev; /* pointer to the parent rc_dev */ - /* edge driver */ - struct timer_list edge_handle; + /* handle delayed ir_raw_event_store_edge processing */ + spinlock_t edge_spinlock; + struct timer_list edge_handle; /* raw decoder state follows */ struct ir_raw_event prev_ev; @@ -117,6 +118,12 @@ struct ir_raw_event_ctrl { unsigned count; u32 durations[16]; } xmp; + struct imon_dec { + int state; + int count; + int last_chk; + unsigned int bits; + } imon; }; /* macros for IR decoders */ @@ -292,11 +299,4 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; } static inline void ir_lirc_unregister(struct rc_dev *dev) { } #endif -/* - * Decoder initialization code - * - * Those load logic are called during ir-core init, and automatically - * loads the compiled decoders for their usage with IR raw events - */ - #endif /* _RC_CORE_PRIV */ diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 18504870b9f0..374f83105a23 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -65,8 +65,8 @@ int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev) if (!dev->raw) return -EINVAL; - IR_dprintk(2, "sample: (%05dus %s)\n", - TO_US(ev->duration), TO_STR(ev->pulse)); + dev_dbg(&dev->dev, "sample: (%05dus %s)\n", + TO_US(ev->duration), TO_STR(ev->pulse)); if (!kfifo_put(&dev->raw->kfifo, *ev)) { dev_err(&dev->dev, "IR event FIFO is full!\n"); @@ -92,7 +92,6 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse) { ktime_t now; DEFINE_IR_RAW_EVENT(ev); - int rc = 0; if (!dev->raw) return -EINVAL; @@ -101,7 +100,33 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse) ev.duration = ktime_to_ns(ktime_sub(now, dev->raw->last_event)); ev.pulse = !pulse; - rc = ir_raw_event_store(dev, &ev); + return ir_raw_event_store_with_timeout(dev, &ev); +} +EXPORT_SYMBOL_GPL(ir_raw_event_store_edge); + +/* + * ir_raw_event_store_with_timeout() - pass a pulse/space duration to the raw + * ir decoders, schedule decoding and + * timeout + * @dev: the struct rc_dev device descriptor + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This routine (which may be called from an interrupt context) stores a + * pulse/space duration for the raw ir decoding state machines, schedules + * decoding and generates a timeout. + */ +int ir_raw_event_store_with_timeout(struct rc_dev *dev, struct ir_raw_event *ev) +{ + ktime_t now; + int rc = 0; + + if (!dev->raw) + return -EINVAL; + + now = ktime_get(); + + spin_lock(&dev->raw->edge_spinlock); + rc = ir_raw_event_store(dev, ev); dev->raw->last_event = now; @@ -112,10 +137,11 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse) mod_timer(&dev->raw->edge_handle, jiffies + msecs_to_jiffies(15)); } + spin_unlock(&dev->raw->edge_spinlock); return rc; } -EXPORT_SYMBOL_GPL(ir_raw_event_store_edge); +EXPORT_SYMBOL_GPL(ir_raw_event_store_with_timeout); /** * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing @@ -168,7 +194,7 @@ void ir_raw_event_set_idle(struct rc_dev *dev, bool idle) if (!dev->raw) return; - IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave"); + dev_dbg(&dev->dev, "%s idle mode\n", idle ? "enter" : "leave"); if (idle) { dev->raw->this_ev.timeout = true; @@ -462,12 +488,26 @@ int ir_raw_encode_scancode(enum rc_proto protocol, u32 scancode, } EXPORT_SYMBOL(ir_raw_encode_scancode); -static void edge_handle(struct timer_list *t) +/** + * ir_raw_edge_handle() - Handle ir_raw_event_store_edge() processing + * + * @t: timer_list + * + * This callback is armed by ir_raw_event_store_edge(). It does two things: + * first of all, rather than calling ir_raw_event_handle() for each + * edge and waking up the rc thread, 15 ms after the first edge + * ir_raw_event_handle() is called. Secondly, generate a timeout event + * no more IR is received after the rc_dev timeout. + */ +static void ir_raw_edge_handle(struct timer_list *t) { struct ir_raw_event_ctrl *raw = from_timer(raw, t, edge_handle); struct rc_dev *dev = raw->dev; - ktime_t interval = ktime_sub(ktime_get(), dev->raw->last_event); + unsigned long flags; + ktime_t interval; + spin_lock_irqsave(&dev->raw->edge_spinlock, flags); + interval = ktime_sub(ktime_get(), dev->raw->last_event); if (ktime_to_ns(interval) >= dev->timeout) { DEFINE_IR_RAW_EVENT(ev); @@ -480,6 +520,7 @@ static void edge_handle(struct timer_list *t) jiffies + nsecs_to_jiffies(dev->timeout - ktime_to_ns(interval))); } + spin_unlock_irqrestore(&dev->raw->edge_spinlock, flags); ir_raw_event_handle(dev); } @@ -528,7 +569,8 @@ int ir_raw_event_prepare(struct rc_dev *dev) dev->raw->dev = dev; dev->change_protocol = change_protocol; - timer_setup(&dev->raw->edge_handle, edge_handle, 0); + spin_lock_init(&dev->raw->edge_spinlock); + timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0); INIT_KFIFO(dev->raw->kfifo); return 0; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 1db8d38fed7c..b67be33bd62f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -68,6 +68,8 @@ static const struct { .scancode_bits = 0x1fff, .repeat_period = 250 }, [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, + [RC_PROTO_IMON] = { .name = "imon", + .scancode_bits = 0x7fffffff, .repeat_period = 250 }, }; /* Used to keep track of known keymaps */ @@ -156,6 +158,7 @@ static struct rc_map_list empty_map = { /** * ir_create_table() - initializes a scancode table + * @dev: the rc_dev device * @rc_map: the rc_map to initialize * @name: name to assign to the table * @rc_proto: ir type to assign to the new table @@ -166,7 +169,7 @@ static struct rc_map_list empty_map = { * * return: zero on success or a negative error code */ -static int ir_create_table(struct rc_map *rc_map, +static int ir_create_table(struct rc_dev *dev, struct rc_map *rc_map, const char *name, u64 rc_proto, size_t size) { rc_map->name = kstrdup(name, GFP_KERNEL); @@ -182,8 +185,8 @@ static int ir_create_table(struct rc_map *rc_map, return -ENOMEM; } - IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", - rc_map->size, rc_map->alloc); + dev_dbg(&dev->dev, "Allocated space for %u keycode entries (%u bytes)\n", + rc_map->size, rc_map->alloc); return 0; } @@ -205,6 +208,7 @@ static void ir_free_table(struct rc_map *rc_map) /** * ir_resize_table() - resizes a scancode table if necessary + * @dev: the rc_dev device * @rc_map: the rc_map to resize * @gfp_flags: gfp flags to use when allocating memory * @@ -213,7 +217,8 @@ static void ir_free_table(struct rc_map *rc_map) * * return: zero on success or a negative error code */ -static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags) +static int ir_resize_table(struct rc_dev *dev, struct rc_map *rc_map, + gfp_t gfp_flags) { unsigned int oldalloc = rc_map->alloc; unsigned int newalloc = oldalloc; @@ -226,23 +231,21 @@ static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags) return -ENOMEM; newalloc *= 2; - IR_dprintk(1, "Growing table to %u bytes\n", newalloc); + dev_dbg(&dev->dev, "Growing table to %u bytes\n", newalloc); } if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) { /* Less than 1/3 of entries in use -> shrink keytable */ newalloc /= 2; - IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc); + dev_dbg(&dev->dev, "Shrinking table to %u bytes\n", newalloc); } if (newalloc == oldalloc) return 0; newscan = kmalloc(newalloc, gfp_flags); - if (!newscan) { - IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc); + if (!newscan) return -ENOMEM; - } memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table)); rc_map->scan = newscan; @@ -275,16 +278,16 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, /* Did the user wish to remove the mapping? */ if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { - IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", - index, rc_map->scan[index].scancode); + dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04x\n", + index, rc_map->scan[index].scancode); rc_map->len--; memmove(&rc_map->scan[index], &rc_map->scan[index+ 1], (rc_map->len - index) * sizeof(struct rc_map_table)); } else { - IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n", - index, - old_keycode == KEY_RESERVED ? "New" : "Replacing", - rc_map->scan[index].scancode, new_keycode); + dev_dbg(&dev->dev, "#%d: %s scan 0x%04x with key 0x%04x\n", + index, + old_keycode == KEY_RESERVED ? "New" : "Replacing", + rc_map->scan[index].scancode, new_keycode); rc_map->scan[index].keycode = new_keycode; __set_bit(new_keycode, dev->input_dev->keybit); } @@ -301,7 +304,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, } /* Possibly shrink the keytable, failure is not a problem */ - ir_resize_table(rc_map, GFP_ATOMIC); + ir_resize_table(dev, rc_map, GFP_ATOMIC); } return old_keycode; @@ -352,7 +355,7 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev, /* No previous mapping found, we might need to grow the table */ if (rc_map->size == rc_map->len) { - if (!resize || ir_resize_table(rc_map, GFP_ATOMIC)) + if (!resize || ir_resize_table(dev, rc_map, GFP_ATOMIC)) return -1U; } @@ -431,8 +434,8 @@ static int ir_setkeytable(struct rc_dev *dev, unsigned int i, index; int rc; - rc = ir_create_table(rc_map, from->name, - from->rc_proto, from->size); + rc = ir_create_table(dev, rc_map, from->name, from->rc_proto, + from->size); if (rc) return rc; @@ -576,8 +579,8 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode) spin_unlock_irqrestore(&rc_map->lock, flags); if (keycode != KEY_RESERVED) - IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", - dev->device_name, scancode, keycode); + dev_dbg(&dev->dev, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->device_name, scancode, keycode); return keycode; } @@ -596,7 +599,7 @@ static void ir_do_keyup(struct rc_dev *dev, bool sync) if (!dev->keypressed) return; - IR_dprintk(1, "keyup key 0x%04x\n", dev->last_keycode); + dev_dbg(&dev->dev, "keyup key 0x%04x\n", dev->last_keycode); del_timer(&dev->timer_repeat); input_report_key(dev->input_dev, dev->last_keycode, 0); led_trigger_event(led_feedback, LED_OFF); @@ -751,8 +754,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, /* Register a keypress */ dev->keypressed = true; - IR_dprintk(1, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", - dev->device_name, keycode, protocol, scancode); + dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", + dev->device_name, keycode, protocol, scancode); input_report_key(dev->input_dev, keycode, 1); led_trigger_event(led_feedback, LED_FULL); @@ -1003,6 +1006,7 @@ static const struct { RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, { RC_PROTO_BIT_CEC, "cec", NULL }, + { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, }; /** @@ -1056,8 +1060,8 @@ static ssize_t show_protocols(struct device *device, mutex_unlock(&dev->lock); - IR_dprintk(1, "%s: allowed - 0x%llx, enabled - 0x%llx\n", - __func__, (long long)allowed, (long long)enabled); + dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - 0x%llx\n", + __func__, (long long)allowed, (long long)enabled); for (i = 0; i < ARRAY_SIZE(proto_names); i++) { if (allowed & enabled & proto_names[i].type) @@ -1083,6 +1087,7 @@ static ssize_t show_protocols(struct device *device, /** * parse_protocol_change() - parses a protocol change request + * @dev: rc_dev device * @protocols: pointer to the bitmask of current protocols * @buf: pointer to the buffer with a list of changes * @@ -1092,7 +1097,8 @@ static ssize_t show_protocols(struct device *device, * Writing "none" will disable all protocols. * Returns the number of changes performed or a negative error code. */ -static int parse_protocol_change(u64 *protocols, const char *buf) +static int parse_protocol_change(struct rc_dev *dev, u64 *protocols, + const char *buf) { const char *tmp; unsigned count = 0; @@ -1128,7 +1134,8 @@ static int parse_protocol_change(u64 *protocols, const char *buf) if (!strcasecmp(tmp, "lirc")) mask = 0; else { - IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); + dev_dbg(&dev->dev, "Unknown protocol: '%s'\n", + tmp); return -EINVAL; } } @@ -1144,7 +1151,7 @@ static int parse_protocol_change(u64 *protocols, const char *buf) } if (!count) { - IR_dprintk(1, "Protocol not specified\n"); + dev_dbg(&dev->dev, "Protocol not specified\n"); return -EINVAL; } @@ -1217,12 +1224,12 @@ static ssize_t store_protocols(struct device *device, u64 old_protocols, new_protocols; ssize_t rc; - IR_dprintk(1, "Normal protocol change requested\n"); + dev_dbg(&dev->dev, "Normal protocol change requested\n"); current_protocols = &dev->enabled_protocols; filter = &dev->scancode_filter; if (!dev->change_protocol) { - IR_dprintk(1, "Protocol switching not supported\n"); + dev_dbg(&dev->dev, "Protocol switching not supported\n"); return -EINVAL; } @@ -1230,14 +1237,14 @@ static ssize_t store_protocols(struct device *device, old_protocols = *current_protocols; new_protocols = old_protocols; - rc = parse_protocol_change(&new_protocols, buf); + rc = parse_protocol_change(dev, &new_protocols, buf); if (rc < 0) goto out; rc = dev->change_protocol(dev, &new_protocols); if (rc < 0) { - IR_dprintk(1, "Error setting protocols to 0x%llx\n", - (long long)new_protocols); + dev_dbg(&dev->dev, "Error setting protocols to 0x%llx\n", + (long long)new_protocols); goto out; } @@ -1246,8 +1253,8 @@ static ssize_t store_protocols(struct device *device, if (new_protocols != old_protocols) { *current_protocols = new_protocols; - IR_dprintk(1, "Protocols changed to 0x%llx\n", - (long long)new_protocols); + dev_dbg(&dev->dev, "Protocols changed to 0x%llx\n", + (long long)new_protocols); } /* @@ -1435,8 +1442,8 @@ static ssize_t show_wakeup_protocols(struct device *device, mutex_unlock(&dev->lock); - IR_dprintk(1, "%s: allowed - 0x%llx, enabled - %d\n", - __func__, (long long)allowed, enabled); + dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - %d\n", + __func__, (long long)allowed, enabled); for (i = 0; i < ARRAY_SIZE(protocols); i++) { if (allowed & (1ULL << i)) { @@ -1511,7 +1518,7 @@ static ssize_t store_wakeup_protocols(struct device *device, if (dev->wakeup_protocol != protocol) { dev->wakeup_protocol = protocol; - IR_dprintk(1, "Wakeup protocol changed to %d\n", protocol); + dev_dbg(&dev->dev, "Wakeup protocol changed to %d\n", protocol); if (protocol == RC_PROTO_RC6_MCE) dev->scancode_wakeup_filter.data = 0x800f0000; @@ -1874,9 +1881,8 @@ int rc_register_device(struct rc_dev *dev) dev->registered = true; - IR_dprintk(1, "Registered rc%u (driver: %s)\n", - dev->minor, - dev->driver_name ? dev->driver_name : "unknown"); + dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor, + dev->driver_name ? dev->driver_name : "unknown"); return 0; @@ -1929,12 +1935,12 @@ void rc_unregister_device(struct rc_dev *dev) if (!dev) return; - del_timer_sync(&dev->timer_keyup); - del_timer_sync(&dev->timer_repeat); - if (dev->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(dev); + del_timer_sync(&dev->timer_keyup); + del_timer_sync(&dev->timer_repeat); + rc_free_rx_device(dev); mutex_lock(&dev->lock); @@ -1994,9 +2000,5 @@ static void __exit rc_core_exit(void) subsys_initcall(rc_core_init); module_exit(rc_core_exit); -int rc_core_debug; /* ir_debug level (0,1,2) */ -EXPORT_SYMBOL_GPL(rc_core_debug); -module_param_named(debug, rc_core_debug, int, 0644); - MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 97f367b446c4..f500cea228a9 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -72,12 +72,8 @@ /* CIR_REG register idle threshold */ #define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8))) -/* Required frequency for IR0 or IR1 clock in CIR mode */ +/* Required frequency for IR0 or IR1 clock in CIR mode (default) */ #define SUNXI_IR_BASE_CLK 8000000 -/* Frequency after IR internal divider */ -#define SUNXI_IR_CLK (SUNXI_IR_BASE_CLK / 64) -/* Sample period in ns */ -#define SUNXI_IR_SAMPLE (1000000000ul / SUNXI_IR_CLK) /* Noise threshold in samples */ #define SUNXI_IR_RXNOISE 1 /* Idle Threshold in samples */ @@ -122,7 +118,8 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) /* for each bit in fifo */ dt = readb(ir->base + SUNXI_IR_RXFIFO_REG); rawir.pulse = (dt & 0x80) != 0; - rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE; + rawir.duration = ((dt & 0x7f) + 1) * + ir->rc->rx_resolution; ir_raw_event_store_with_filter(ir->rc, &rawir); } } @@ -148,6 +145,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) struct device_node *dn = dev->of_node; struct resource *res; struct sunxi_ir *ir; + u32 b_clk_freq = SUNXI_IR_BASE_CLK; ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL); if (!ir) @@ -172,6 +170,9 @@ static int sunxi_ir_probe(struct platform_device *pdev) return PTR_ERR(ir->clk); } + /* Base clock frequency (optional) */ + of_property_read_u32(dn, "clock-frequency", &b_clk_freq); + /* Reset (optional) */ ir->rst = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(ir->rst)) @@ -180,11 +181,12 @@ static int sunxi_ir_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK); + ret = clk_set_rate(ir->clk, b_clk_freq); if (ret) { dev_err(dev, "set ir base clock failed!\n"); goto exit_reset_assert; } + dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq); if (clk_prepare_enable(ir->apb_clk)) { dev_err(dev, "try to enable apb_ir_clk failed\n"); @@ -225,7 +227,8 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - ir->rc->rx_resolution = SUNXI_IR_SAMPLE; + /* Frequency after IR internal divider with sample period in ns */ + ir->rc->rx_resolution = (1000000000ul / (b_clk_freq / 64)); ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); ir->rc->driver_name = SUNXI_IR_DEV; diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index a21f5a39a440..b07ac86fc53c 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -12,3 +12,17 @@ config VIDEO_GS1662 endmenu endif + +if SPI +menu "Media SPI Adapters" + +config CXD2880_SPI_DRV + tristate "Sony CXD2880 SPI support" + depends on DVB_CORE && SPI + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Choose if you would like to have SPI interface support for Sony CXD2880. + +endmenu + +endif diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile index ea64013d16cc..9e536777a330 100644 --- a/drivers/media/spi/Makefile +++ b/drivers/media/spi/Makefile @@ -1 +1,6 @@ obj-$(CONFIG_VIDEO_GS1662) += gs1662.o +obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends +ccflags-y += -Idrivers/media/dvb-frontends/cxd2880 diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c new file mode 100644 index 000000000000..4df3bd312f48 --- /dev/null +++ b/drivers/media/spi/cxd2880-spi.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880-spi.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI adapter + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/spi/spi.h> +#include <linux/ktime.h> + +#include <media/dvb_demux.h> +#include <media/dmxdev.h> +#include <media/dvb_frontend.h> +#include "cxd2880.h" + +#define CXD2880_MAX_FILTER_SIZE 32 +#define BURST_WRITE_MAX 128 +#define MAX_TRANS_PKT 300 + +struct cxd2880_ts_buf_info { + u8 read_ready:1; + u8 almost_full:1; + u8 almost_empty:1; + u8 overflow:1; + u8 underflow:1; + u16 pkt_num; +}; + +struct cxd2880_pid_config { + u8 is_enable; + u16 pid; +}; + +struct cxd2880_pid_filter_config { + u8 is_negative; + struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE]; +}; + +struct cxd2880_dvb_spi { + struct dvb_frontend dvb_fe; + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend dmx_fe; + struct task_struct *cxd2880_ts_read_thread; + struct spi_device *spi; + struct mutex spi_mutex; /* For SPI access exclusive control */ + int feed_count; + int all_pid_feed_count; + u8 *ts_buf; + struct cxd2880_pid_filter_config filter_config; +}; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size) +{ + struct spi_message msg; + struct spi_transfer tx; + + if (!spi || !data) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + memset(&tx, 0, sizeof(tx)); + tx.tx_buf = data; + tx.len = size; + + spi_message_init(&msg); + spi_message_add_tail(&tx, &msg); + + return spi_sync(spi, &msg); +} + +static int cxd2880_write_reg(struct spi_device *spi, + u8 sub_address, const u8 *data, u32 size) +{ + u8 send_data[BURST_WRITE_MAX + 4]; + const u8 *write_data_top = NULL; + int ret = 0; + + if (!spi || !data) { + pr_err("invalid arg\n"); + return -EINVAL; + } + if (size > BURST_WRITE_MAX) { + pr_err("data size > WRITE_MAX\n"); + return -EINVAL; + } + + if (sub_address + size > 0x100) { + pr_err("out of range\n"); + return -EINVAL; + } + + send_data[0] = 0x0e; + write_data_top = data; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = (u8)size; + + memcpy(&send_data[3], write_data_top, send_data[2]); + + ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3); + if (ret) { + pr_err("write spi failed %d\n", ret); + break; + } + sub_address += send_data[2]; + write_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +static int cxd2880_spi_read_ts(struct spi_device *spi, + u8 *read_data, + u32 packet_num) +{ + int ret; + u8 data[3]; + struct spi_message message; + struct spi_transfer transfer[2]; + + if (!spi || !read_data || !packet_num) { + pr_err("invalid arg\n"); + return -EINVAL; + } + if (packet_num > 0xffff) { + pr_err("packet num > 0xffff\n"); + return -EINVAL; + } + + data[0] = 0x10; + data[1] = packet_num >> 8; + data[2] = packet_num; + + spi_message_init(&message); + memset(transfer, 0, sizeof(transfer)); + + transfer[0].len = 3; + transfer[0].tx_buf = data; + spi_message_add_tail(&transfer[0], &message); + transfer[1].len = packet_num * 188; + transfer[1].rx_buf = read_data; + spi_message_add_tail(&transfer[1], &message); + + ret = spi_sync(spi, &message); + if (ret) + pr_err("spi_write_then_read failed\n"); + + return ret; +} + +static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi, + struct cxd2880_ts_buf_info *info) +{ + u8 send_data = 0x20; + u8 recv_data[2]; + int ret; + + if (!spi || !info) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + ret = spi_write_then_read(spi, &send_data, 1, + recv_data, sizeof(recv_data)); + if (ret) + pr_err("spi_write_then_read failed\n"); + + info->read_ready = (recv_data[0] & 0x80) ? 1 : 0; + info->almost_full = (recv_data[0] & 0x40) ? 1 : 0; + info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0; + info->overflow = (recv_data[0] & 0x10) ? 1 : 0; + info->underflow = (recv_data[0] & 0x08) ? 1 : 0; + info->pkt_num = ((recv_data[0] & 0x07) << 8) | recv_data[1]; + + return ret; +} + +static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi) +{ + u8 data = 0x03; + int ret; + + ret = cxd2880_write_spi(spi, &data, 1); + + if (ret) + pr_err("write spi failed\n"); + + return ret; +} + +static int cxd2880_set_pid_filter(struct spi_device *spi, + struct cxd2880_pid_filter_config *cfg) +{ + u8 data[65]; + int i; + u16 pid = 0; + int ret; + + if (!spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + data[0] = 0x00; + ret = cxd2880_write_reg(spi, 0x00, &data[0], 1); + if (ret) + return ret; + if (!cfg) { + data[0] = 0x02; + ret = cxd2880_write_reg(spi, 0x50, &data[0], 1); + } else { + data[0] = cfg->is_negative ? 0x01 : 0x00; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + pid = cfg->pid_config[i].pid; + if (cfg->pid_config[i].is_enable) { + data[1 + (i * 2)] = (pid >> 8) | 0x20; + data[2 + (i * 2)] = pid & 0xff; + } else { + data[1 + (i * 2)] = 0x00; + data[2 + (i * 2)] = 0x00; + } + } + ret = cxd2880_write_reg(spi, 0x50, data, 65); + } + + return ret; +} + +static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi, + struct cxd2880_pid_filter_config *cfg, + bool is_all_pid_filter) +{ + int ret; + + if (!dvb_spi || !cfg) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + mutex_lock(&dvb_spi->spi_mutex); + if (is_all_pid_filter) { + struct cxd2880_pid_filter_config tmpcfg; + + memset(&tmpcfg, 0, sizeof(tmpcfg)); + tmpcfg.is_negative = 1; + tmpcfg.pid_config[0].is_enable = 1; + tmpcfg.pid_config[0].pid = 0x1fff; + + ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg); + } else { + ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg); + } + mutex_unlock(&dvb_spi->spi_mutex); + + if (ret) + pr_err("set_pid_filter failed\n"); + + return ret; +} + +static int cxd2880_ts_read(void *arg) +{ + struct cxd2880_dvb_spi *dvb_spi = NULL; + struct cxd2880_ts_buf_info info; + ktime_t start; + u32 i; + int ret; + + dvb_spi = arg; + if (!dvb_spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi); + if (ret) { + pr_err("set_clear_ts_buffer failed\n"); + return ret; + } + + start = ktime_get(); + while (!kthread_should_stop()) { + ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi, + &info); + if (ret) { + pr_err("spi_read_ts_buffer_info error\n"); + return ret; + } + + if (info.pkt_num > MAX_TRANS_PKT) { + for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) { + cxd2880_spi_read_ts(dvb_spi->spi, + dvb_spi->ts_buf, + MAX_TRANS_PKT); + dvb_dmx_swfilter(&dvb_spi->demux, + dvb_spi->ts_buf, + MAX_TRANS_PKT * 188); + } + start = ktime_get(); + } else if ((info.pkt_num > 0) && + (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) { + cxd2880_spi_read_ts(dvb_spi->spi, + dvb_spi->ts_buf, + info.pkt_num); + dvb_dmx_swfilter(&dvb_spi->demux, + dvb_spi->ts_buf, + info.pkt_num * 188); + start = ktime_get(); + } else { + usleep_range(10000, 11000); + } + } + + return 0; +} + +static int cxd2880_start_feed(struct dvb_demux_feed *feed) +{ + int ret = 0; + int i = 0; + struct dvb_demux *demux = NULL; + struct cxd2880_dvb_spi *dvb_spi = NULL; + + if (!feed) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + demux = feed->demux; + if (!demux) { + pr_err("feed->demux is NULL\n"); + return -EINVAL; + } + dvb_spi = demux->priv; + + if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) { + pr_err("Exceeded maximum PID count (32)."); + pr_err("Selected PID cannot be enabled.\n"); + return -EINVAL; + } + + if (feed->pid == 0x2000) { + if (dvb_spi->all_pid_feed_count == 0) { + ret = cxd2880_update_pid_filter(dvb_spi, + &dvb_spi->filter_config, + true); + if (ret) { + pr_err("update pid filter failed\n"); + return ret; + } + } + dvb_spi->all_pid_feed_count++; + + pr_debug("all PID feed (count = %d)\n", + dvb_spi->all_pid_feed_count); + } else { + struct cxd2880_pid_filter_config cfgtmp; + + cfgtmp = dvb_spi->filter_config; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + if (cfgtmp.pid_config[i].is_enable == 0) { + cfgtmp.pid_config[i].is_enable = 1; + cfgtmp.pid_config[i].pid = feed->pid; + pr_debug("store PID %d to #%d\n", + feed->pid, i); + break; + } + } + if (i == CXD2880_MAX_FILTER_SIZE) { + pr_err("PID filter is full. Assumed bug.\n"); + return -EINVAL; + } + if (!dvb_spi->all_pid_feed_count) + ret = cxd2880_update_pid_filter(dvb_spi, + &cfgtmp, + false); + if (ret) + return ret; + + dvb_spi->filter_config = cfgtmp; + } + + if (dvb_spi->feed_count == 0) { + dvb_spi->ts_buf = + kmalloc(MAX_TRANS_PKT * 188, + GFP_KERNEL | GFP_DMA); + if (!dvb_spi->ts_buf) { + pr_err("ts buffer allocate failed\n"); + memset(&dvb_spi->filter_config, 0, + sizeof(dvb_spi->filter_config)); + dvb_spi->all_pid_feed_count = 0; + return -ENOMEM; + } + dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read, + dvb_spi, + "cxd2880_ts_read"); + if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) { + pr_err("kthread_run failed/\n"); + kfree(dvb_spi->ts_buf); + dvb_spi->ts_buf = NULL; + memset(&dvb_spi->filter_config, 0, + sizeof(dvb_spi->filter_config)); + dvb_spi->all_pid_feed_count = 0; + return PTR_ERR(dvb_spi->cxd2880_ts_read_thread); + } + } + + dvb_spi->feed_count++; + + pr_debug("start feed (count %d)\n", dvb_spi->feed_count); + return 0; +} + +static int cxd2880_stop_feed(struct dvb_demux_feed *feed) +{ + int i = 0; + int ret; + struct dvb_demux *demux = NULL; + struct cxd2880_dvb_spi *dvb_spi = NULL; + + if (!feed) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + demux = feed->demux; + if (!demux) { + pr_err("feed->demux is NULL\n"); + return -EINVAL; + } + dvb_spi = demux->priv; + + if (!dvb_spi->feed_count) { + pr_err("no feed is started\n"); + return -EINVAL; + } + + if (feed->pid == 0x2000) { + /* + * Special PID case. + * Number of 0x2000 feed request was stored + * in dvb_spi->all_pid_feed_count. + */ + if (dvb_spi->all_pid_feed_count <= 0) { + pr_err("PID %d not found.\n", feed->pid); + return -EINVAL; + } + dvb_spi->all_pid_feed_count--; + } else { + struct cxd2880_pid_filter_config cfgtmp; + + cfgtmp = dvb_spi->filter_config; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + if (feed->pid == cfgtmp.pid_config[i].pid && + cfgtmp.pid_config[i].is_enable != 0) { + cfgtmp.pid_config[i].is_enable = 0; + cfgtmp.pid_config[i].pid = 0; + pr_debug("removed PID %d from #%d\n", + feed->pid, i); + break; + } + } + dvb_spi->filter_config = cfgtmp; + + if (i == CXD2880_MAX_FILTER_SIZE) { + pr_err("PID %d not found\n", feed->pid); + return -EINVAL; + } + } + + ret = cxd2880_update_pid_filter(dvb_spi, + &dvb_spi->filter_config, + dvb_spi->all_pid_feed_count > 0); + dvb_spi->feed_count--; + + if (dvb_spi->feed_count == 0) { + int ret_stop = 0; + + ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread); + if (ret_stop) { + pr_err("'kthread_stop failed. (%d)\n", ret_stop); + ret = ret_stop; + } + kfree(dvb_spi->ts_buf); + dvb_spi->ts_buf = NULL; + } + + pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count); + + return ret; +} + +static const struct of_device_id cxd2880_spi_of_match[] = { + { .compatible = "sony,cxd2880" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match); + +static int +cxd2880_spi_probe(struct spi_device *spi) +{ + int ret; + struct cxd2880_dvb_spi *dvb_spi = NULL; + struct cxd2880_config config; + + if (!spi) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL); + if (!dvb_spi) + return -ENOMEM; + + dvb_spi->spi = spi; + mutex_init(&dvb_spi->spi_mutex); + dev_set_drvdata(&spi->dev, dvb_spi); + config.spi = spi; + config.spi_mutex = &dvb_spi->spi_mutex; + + ret = dvb_register_adapter(&dvb_spi->adapter, + "CXD2880", + THIS_MODULE, + &spi->dev, + adapter_nr); + if (ret < 0) { + pr_err("dvb_register_adapter() failed\n"); + goto fail_adapter; + } + + if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) { + pr_err("cxd2880_attach failed\n"); + goto fail_attach; + } + + ret = dvb_register_frontend(&dvb_spi->adapter, + &dvb_spi->dvb_fe); + if (ret < 0) { + pr_err("dvb_register_frontend() failed\n"); + goto fail_frontend; + } + + dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING; + dvb_spi->demux.priv = dvb_spi; + dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->demux.start_feed = cxd2880_start_feed; + dvb_spi->demux.stop_feed = cxd2880_stop_feed; + + ret = dvb_dmx_init(&dvb_spi->demux); + if (ret < 0) { + pr_err("dvb_dmx_init() failed\n"); + goto fail_dmx; + } + + dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx; + dvb_spi->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&dvb_spi->dmxdev, + &dvb_spi->adapter); + if (ret < 0) { + pr_err("dvb_dmxdev_init() failed\n"); + goto fail_dmxdev; + } + + dvb_spi->dmx_fe.source = DMX_FRONTEND_0; + ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + if (ret < 0) { + pr_err("add_frontend() failed\n"); + goto fail_dmx_fe; + } + + ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + if (ret < 0) { + pr_err("dvb_register_frontend() failed\n"); + goto fail_fe_conn; + } + + pr_info("Sony CXD2880 has successfully attached.\n"); + + return 0; + +fail_fe_conn: + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); +fail_dmx_fe: + dvb_dmxdev_release(&dvb_spi->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb_spi->demux); +fail_dmx: + dvb_unregister_frontend(&dvb_spi->dvb_fe); +fail_frontend: + dvb_frontend_detach(&dvb_spi->dvb_fe); +fail_attach: + dvb_unregister_adapter(&dvb_spi->adapter); +fail_adapter: + kfree(dvb_spi); + return ret; +} + +static int +cxd2880_spi_remove(struct spi_device *spi) +{ + struct cxd2880_dvb_spi *dvb_spi; + + if (!spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + dvb_spi = dev_get_drvdata(&spi->dev); + + if (!dvb_spi) { + pr_err("failed\n"); + return -EINVAL; + } + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + dvb_dmxdev_release(&dvb_spi->dmxdev); + dvb_dmx_release(&dvb_spi->demux); + dvb_unregister_frontend(&dvb_spi->dvb_fe); + dvb_frontend_detach(&dvb_spi->dvb_fe); + dvb_unregister_adapter(&dvb_spi->adapter); + + kfree(dvb_spi); + pr_info("cxd2880_spi remove ok.\n"); + + return 0; +} + +static const struct spi_device_id cxd2880_spi_id[] = { + { "cxd2880", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id); + +static struct spi_driver cxd2880_spi_driver = { + .driver = { + .name = "cxd2880", + .of_match_table = cxd2880_spi_of_match, + }, + .id_table = cxd2880_spi_id, + .probe = cxd2880_spi_probe, + .remove = cxd2880_spi_remove, +}; +module_spi_driver(cxd2880_spi_driver); + +MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver SPI adapter"); +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 564a000f503e..b5b9d87ba75c 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -293,28 +293,18 @@ static inline struct e4000_dev *e4000_subdev_to_dev(struct v4l2_subdev *sd) return container_of(sd, struct e4000_dev, sd); } -static int e4000_s_power(struct v4l2_subdev *sd, int on) +static int e4000_standby(struct v4l2_subdev *sd) { struct e4000_dev *dev = e4000_subdev_to_dev(sd); - struct i2c_client *client = dev->client; int ret; - dev_dbg(&client->dev, "on=%d\n", on); - - if (on) - ret = e4000_init(dev); - else - ret = e4000_sleep(dev); + ret = e4000_sleep(dev); if (ret) return ret; return e4000_set_params(dev); } -static const struct v4l2_subdev_core_ops e4000_subdev_core_ops = { - .s_power = e4000_s_power, -}; - static int e4000_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) { struct e4000_dev *dev = e4000_subdev_to_dev(sd); @@ -382,6 +372,7 @@ static int e4000_enum_freq_bands(struct v4l2_subdev *sd, } static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = { + .standby = e4000_standby, .g_tuner = e4000_g_tuner, .s_tuner = e4000_s_tuner, .g_frequency = e4000_g_frequency, @@ -390,7 +381,6 @@ static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = { }; static const struct v4l2_subdev_ops e4000_subdev_ops = { - .core = &e4000_subdev_core_ops, .tuner = &e4000_subdev_tuner_ops, }; diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index f4d4665de168..743184ae0d26 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -386,28 +386,18 @@ static inline struct fc2580_dev *fc2580_subdev_to_dev(struct v4l2_subdev *sd) return container_of(sd, struct fc2580_dev, subdev); } -static int fc2580_s_power(struct v4l2_subdev *sd, int on) +static int fc2580_standby(struct v4l2_subdev *sd) { struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); - struct i2c_client *client = dev->client; int ret; - dev_dbg(&client->dev, "on=%d\n", on); - - if (on) - ret = fc2580_init(dev); - else - ret = fc2580_sleep(dev); + ret = fc2580_sleep(dev); if (ret) return ret; return fc2580_set_params(dev); } -static const struct v4l2_subdev_core_ops fc2580_subdev_core_ops = { - .s_power = fc2580_s_power, -}; - static int fc2580_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) { struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); @@ -475,6 +465,7 @@ static int fc2580_enum_freq_bands(struct v4l2_subdev *sd, } static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = { + .standby = fc2580_standby, .g_tuner = fc2580_g_tuner, .s_tuner = fc2580_s_tuner, .g_frequency = fc2580_g_frequency, @@ -483,7 +474,6 @@ static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = { }; static const struct v4l2_subdev_ops fc2580_subdev_ops = { - .core = &fc2580_subdev_core_ops, .tuner = &fc2580_subdev_tuner_ops, }; diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c index 3a12ef35682b..5de6ed728708 100644 --- a/drivers/media/tuners/msi001.c +++ b/drivers/media/tuners/msi001.c @@ -291,26 +291,13 @@ err: return ret; } -static int msi001_s_power(struct v4l2_subdev *sd, int on) +static int msi001_standby(struct v4l2_subdev *sd) { struct msi001_dev *dev = sd_to_msi001_dev(sd); - struct spi_device *spi = dev->spi; - int ret; - - dev_dbg(&spi->dev, "on=%d\n", on); - - if (on) - ret = 0; - else - ret = msi001_wreg(dev, 0x000000); - return ret; + return msi001_wreg(dev, 0x000000); } -static const struct v4l2_subdev_core_ops msi001_core_ops = { - .s_power = msi001_s_power, -}; - static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) { struct msi001_dev *dev = sd_to_msi001_dev(sd); @@ -386,6 +373,7 @@ static int msi001_enum_freq_bands(struct v4l2_subdev *sd, } static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = { + .standby = msi001_standby, .g_tuner = msi001_g_tuner, .s_tuner = msi001_s_tuner, .g_frequency = msi001_g_frequency, @@ -394,7 +382,6 @@ static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = { }; static const struct v4l2_subdev_ops msi001_ops = { - .core = &msi001_core_ops, .tuner = &msi001_tuner_ops, }; diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 70521e0b4c53..65fc067eb864 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -1,10 +1,10 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB + depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 select I2C_ALGOBIT select VIDEO_TVEEPROM - select VIDEOBUF2_VMALLOC + select VIDEOBUF2_VMALLOC if VIDEO_V4L2 select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT @@ -18,7 +18,8 @@ config VIDEO_AU0828 config VIDEO_AU0828_V4L2 bool "Auvitek AU0828 v4l2 analog video support" - depends on VIDEO_AU0828 && VIDEO_V4L2 + depends on VIDEO_AU0828 + depends on VIDEO_V4L2=y || VIDEO_V4L2=VIDEO_AU0828 select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TUNER default y diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index c765d546114d..964cd7bcdd2c 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1091,8 +1091,8 @@ static int au0828_v4l2_close(struct file *filp) */ ret = v4l_enable_media_source(vdev); if (ret == 0) - v4l2_device_call_all(&dev->v4l2_dev, 0, core, - s_power, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, + standby); dev->std_set_in_tuner_core = 0; /* When close the device, set the usb intf0 into alt0 to free diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index f3a1e5b1e57c..b51fc372ca25 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -910,9 +910,6 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) wake_up_interruptible(&cam->wq_stream); } - DBG("Releasing interface\n"); - usb_driver_release_interface(&cpia2_driver, intf); - LOG("CPiA2 camera disconnected.\n"); } diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index f9ec7fedcd5b..c76b2101193c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -922,6 +922,85 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_HAUPPAUGE_935C] = { + .name = "Hauppauge WinTV-HVR-935C", + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = I2C_1_MUX_3, + .demod_i2c_master = I2C_1_MUX_3, + .has_dvb = 1, + .demod_addr = 0x64, /* 0xc8 >> 1 */ + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_HAUPPAUGE_975] = { + .name = "Hauppauge WinTV-HVR-975", + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = I2C_1_MUX_3, + .demod_i2c_master = I2C_1_MUX_3, + .has_dvb = 1, + .demod_addr = 0x59, /* 0xb2 >> 1 */ + .demod_addr2 = 0x64, /* 0xc8 >> 1 */ + .norm = V4L2_STD_ALL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -953,6 +1032,10 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xb123), .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q}, + {USB_DEVICE(0x2040, 0xb151), + .driver_info = CX231XX_BOARD_HAUPPAUGE_935C}, + {USB_DEVICE(0x2040, 0xb150), + .driver_info = CX231XX_BOARD_HAUPPAUGE_975}, {USB_DEVICE(0x2040, 0xb130), .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, {USB_DEVICE(0x2040, 0xb131), @@ -1135,7 +1218,7 @@ static void cx231xx_config_tuner(struct cx231xx *dev) static int read_eeprom(struct cx231xx *dev, struct i2c_client *client, u8 *eedata, int len) { - int ret = 0; + int ret; u8 start_offset = 0; int len_todo = len; u8 *eedata_cur = eedata; @@ -1211,6 +1294,8 @@ void cx231xx_card_setup(struct cx231xx *dev) case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_975: { struct eeprom { struct tveeprom tvee; diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index fb5654062b1a..713029420fcf 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -53,9 +53,10 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define CX231XX_DVB_NUM_BUFS 5 #define CX231XX_DVB_MAX_PACKETSIZE 564 #define CX231XX_DVB_MAX_PACKETS 64 +#define CX231XX_DVB_MAX_FRONTENDS 2 struct cx231xx_dvb { - struct dvb_frontend *frontend; + struct dvb_frontend *frontend[CX231XX_DVB_MAX_FRONTENDS]; /* feed count management */ struct mutex lock; @@ -68,7 +69,7 @@ struct cx231xx_dvb { struct dmx_frontend fe_hw; struct dmx_frontend fe_mem; struct dvb_net net; - struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_demod[2]; struct i2c_client *i2c_client_tuner; }; @@ -79,7 +80,7 @@ static struct s5h1432_config dvico_s5h1432_config = { .vsb_if = S5H1432_IF_4000, .inversion = S5H1432_INVERSION_OFF, .status_mode = S5H1432_DEMODLOCKING, - .mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = { @@ -108,7 +109,7 @@ static struct s5h1411_config tda18271_s5h1411_config = { .qam_if = S5H1411_IF_4000, .inversion = S5H1411_INVERSION_ON, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct s5h1411_config xc5000_s5h1411_config = { .output_mode = S5H1411_SERIAL_OUTPUT, @@ -117,7 +118,7 @@ static struct s5h1411_config xc5000_s5h1411_config = { .qam_if = S5H1411_IF_3250, .inversion = S5H1411_INVERSION_OFF, .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, }; static struct lgdt3305_config hcw_lgdt3305_config = { @@ -386,17 +387,17 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev) cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master); cfg.i2c_addr = addr; - if (!dev->dvb->frontend) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "%s/2: dvb frontend not attached. Can't attach xc5000\n", dev->name); return -EINVAL; } - fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg); + fe = dvb_attach(xc5000_attach, dev->dvb->frontend[0], &cfg); if (!fe) { dev_err(dev->dev, "%s/2: xc5000 attach failed\n", dev->name); - dvb_frontend_detach(dev->dvb->frontend); - dev->dvb->frontend = NULL; + dvb_frontend_detach(dev->dvb->frontend[0]); + dev->dvb->frontend[0] = NULL; return -EINVAL; } @@ -408,9 +409,9 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev) int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) { - if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + if (dev->dvb && dev->dvb->frontend[0]) { - struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops; if (dops->set_analog_params != NULL) { struct analog_parameters params; @@ -421,7 +422,7 @@ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) /*params.audmode = ; */ /* Set the analog parameters to set the frequency */ - dops->set_analog_params(dev->dvb->frontend, ¶ms); + dops->set_analog_params(dev->dvb->frontend[0], ¶ms); } } @@ -433,15 +434,15 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev) { int status = 0; - if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + if (dev->dvb && dev->dvb->frontend[0]) { - struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops; if (dops->init != NULL && !dev->xc_fw_load_done) { dev_dbg(dev->dev, "Reloading firmware for XC5000\n"); - status = dops->init(dev->dvb->frontend); + status = dops->init(dev->dvb->frontend[0]); if (status == 0) { dev->xc_fw_load_done = 1; dev_dbg(dev->dev, @@ -481,17 +482,32 @@ static int register_dvb(struct cx231xx_dvb *dvb, dvb_register_media_controller(&dvb->adapter, dev->media_dev); /* Ensure all frontends negotiate bus access */ - dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; + dvb->frontend[0]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; + if (dvb->frontend[1]) + dvb->frontend[1]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; dvb->adapter.priv = dev; /* register frontend */ - result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + result = dvb_register_frontend(&dvb->adapter, dvb->frontend[0]); if (result < 0) { dev_warn(dev->dev, "%s: dvb_register_frontend failed (errno = %d)\n", dev->name, result); - goto fail_frontend; + goto fail_frontend0; + } + + if (dvb->frontend[1]) { + result = dvb_register_frontend(&dvb->adapter, dvb->frontend[1]); + if (result < 0) { + dev_warn(dev->dev, + "%s: 2nd dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend1; + } + + /* MFE lock */ + dvb->adapter.mfe_shared = 1; } /* register demux stuff */ @@ -569,9 +585,14 @@ fail_fe_hw: fail_dmxdev: dvb_dmx_release(&dvb->demux); fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); + if (dvb->frontend[1]) + dvb_unregister_frontend(dvb->frontend[1]); + dvb_unregister_frontend(dvb->frontend[0]); +fail_frontend1: + if (dvb->frontend[1]) + dvb_frontend_detach(dvb->frontend[1]); +fail_frontend0: + dvb_frontend_detach(dvb->frontend[0]); dvb_unregister_adapter(&dvb->adapter); fail_adapter: return result; @@ -585,8 +606,12 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); + if (dvb->frontend[1]) + dvb_unregister_frontend(dvb->frontend[1]); + dvb_unregister_frontend(dvb->frontend[0]); + if (dvb->frontend[1]) + dvb_frontend_detach(dvb->frontend[1]); + dvb_frontend_detach(dvb->frontend[0]); dvb_unregister_adapter(&dvb->adapter); /* remove I2C tuner */ client = dvb->i2c_client_tuner; @@ -595,7 +620,12 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) i2c_unregister_device(client); } /* remove I2C demod */ - client = dvb->i2c_client_demod; + client = dvb->i2c_client_demod[1]; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + client = dvb->i2c_client_demod[0]; if (client) { module_put(client->dev.driver->owner); i2c_unregister_device(client); @@ -604,7 +634,7 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) static int dvb_init(struct cx231xx *dev) { - int result = 0; + int result; struct cx231xx_dvb *dvb; struct i2c_adapter *tuner_i2c; struct i2c_adapter *demod_i2c; @@ -635,11 +665,11 @@ static int dvb_init(struct cx231xx *dev) case CX231XX_BOARD_CNXT_CARRAERA: case CX231XX_BOARD_CNXT_RDE_250: - dev->dvb->frontend = dvb_attach(s5h1432_attach, + dev->dvb->frontend[0] = dvb_attach(s5h1432_attach, &dvico_s5h1432_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach s5h1432 front end\n"); result = -EINVAL; @@ -647,9 +677,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - if (!dvb_attach(xc5000_attach, dev->dvb->frontend, + if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0], tuner_i2c, &cnxt_rde250_tunerconfig)) { result = -EINVAL; @@ -660,11 +690,11 @@ static int dvb_init(struct cx231xx *dev) case CX231XX_BOARD_CNXT_SHELBY: case CX231XX_BOARD_CNXT_RDU_250: - dev->dvb->frontend = dvb_attach(s5h1411_attach, + dev->dvb->frontend[0] = dvb_attach(s5h1411_attach, &xc5000_s5h1411_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach s5h1411 front end\n"); result = -EINVAL; @@ -672,9 +702,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - if (!dvb_attach(xc5000_attach, dev->dvb->frontend, + if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0], tuner_i2c, &cnxt_rdu250_tunerconfig)) { result = -EINVAL; @@ -683,11 +713,11 @@ static int dvb_init(struct cx231xx *dev) break; case CX231XX_BOARD_CNXT_RDE_253S: - dev->dvb->frontend = dvb_attach(s5h1432_attach, + dev->dvb->frontend[0] = dvb_attach(s5h1432_attach, &dvico_s5h1432_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach s5h1432 front end\n"); result = -EINVAL; @@ -695,9 +725,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - if (!dvb_attach(tda18271_attach, dev->dvb->frontend, + if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], 0x60, tuner_i2c, &cnxt_rde253s_tunerconfig)) { result = -EINVAL; @@ -707,11 +737,11 @@ static int dvb_init(struct cx231xx *dev) case CX231XX_BOARD_CNXT_RDU_253S: case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID: - dev->dvb->frontend = dvb_attach(s5h1411_attach, + dev->dvb->frontend[0] = dvb_attach(s5h1411_attach, &tda18271_s5h1411_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach s5h1411 front end\n"); result = -EINVAL; @@ -719,9 +749,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - if (!dvb_attach(tda18271_attach, dev->dvb->frontend, + if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], 0x60, tuner_i2c, &cnxt_rde253s_tunerconfig)) { result = -EINVAL; @@ -734,11 +764,11 @@ static int dvb_init(struct cx231xx *dev) "%s: looking for tuner / demod on i2c bus: %d\n", __func__, i2c_adapter_id(tuner_i2c)); - dev->dvb->frontend = dvb_attach(lgdt3305_attach, + dev->dvb->frontend[0] = dvb_attach(lgdt3305_attach, &hcw_lgdt3305_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach LG3305 front end\n"); result = -EINVAL; @@ -746,9 +776,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - dvb_attach(tda18271_attach, dev->dvb->frontend, + dvb_attach(tda18271_attach, dev->dvb->frontend[0], 0x60, tuner_i2c, &hcw_tda18271_config); break; @@ -761,7 +791,7 @@ static int dvb_init(struct cx231xx *dev) /* attach demod */ memset(&si2165_pdata, 0, sizeof(si2165_pdata)); - si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.fe = &dev->dvb->frontend[0]; si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL; si2165_pdata.ref_freq_hz = 16000000; @@ -771,7 +801,7 @@ static int dvb_init(struct cx231xx *dev) info.platform_data = &si2165_pdata; request_module(info.type); client = i2c_new_device(demod_i2c, &info); - if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { + if (!client || !client->dev.driver || !dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; @@ -784,14 +814,14 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dvb->i2c_client_demod = client; + dvb->i2c_client_demod[0] = client; - dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; + dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - dvb_attach(tda18271_attach, dev->dvb->frontend, + dvb_attach(tda18271_attach, dev->dvb->frontend[0], 0x60, tuner_i2c, &hcw_tda18271_config); @@ -808,7 +838,7 @@ static int dvb_init(struct cx231xx *dev) /* attach demod */ memset(&si2165_pdata, 0, sizeof(si2165_pdata)); - si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.fe = &dev->dvb->frontend[0]; si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT; si2165_pdata.ref_freq_hz = 24000000; @@ -818,7 +848,7 @@ static int dvb_init(struct cx231xx *dev) info.platform_data = &si2165_pdata; request_module(info.type); client = i2c_new_device(demod_i2c, &info); - if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { + if (!client || !client->dev.driver || !dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; @@ -831,18 +861,18 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dvb->i2c_client_demod = client; + dvb->i2c_client_demod[0] = client; memset(&info, 0, sizeof(struct i2c_board_info)); - dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; + dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = dev->dvb->frontend; + si2157_config.fe = dev->dvb->frontend[0]; #ifdef CONFIG_MEDIA_CONTROLLER_DVB si2157_config.mdev = dev->media_dev; #endif @@ -857,14 +887,14 @@ static int dvb_init(struct cx231xx *dev) tuner_i2c, &info); if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dev->dvb->frontend); + dvb_frontend_detach(dev->dvb->frontend[0]); result = -ENODEV; goto out_free; } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - dvb_frontend_detach(dev->dvb->frontend); + dvb_frontend_detach(dev->dvb->frontend[0]); result = -ENODEV; goto out_free; } @@ -882,26 +912,26 @@ static int dvb_init(struct cx231xx *dev) memset(&info, 0, sizeof(struct i2c_board_info)); - dev->dvb->frontend = dvb_attach(lgdt3306a_attach, + dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach, &hauppauge_955q_lgdt3306a_config, demod_i2c ); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach LGDT3306A frontend.\n"); result = -EINVAL; goto out_free; } - dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; + dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = dev->dvb->frontend; + si2157_config.fe = dev->dvb->frontend[0]; #ifdef CONFIG_MEDIA_CONTROLLER_DVB si2157_config.mdev = dev->media_dev; #endif @@ -916,14 +946,14 @@ static int dvb_init(struct cx231xx *dev) tuner_i2c, &info); if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dev->dvb->frontend); + dvb_frontend_detach(dev->dvb->frontend[0]); result = -ENODEV; goto out_free; } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - dvb_frontend_detach(dev->dvb->frontend); + dvb_frontend_detach(dev->dvb->frontend[0]); result = -ENODEV; goto out_free; } @@ -940,11 +970,11 @@ static int dvb_init(struct cx231xx *dev) "%s: looking for demod on i2c bus: %d\n", __func__, i2c_adapter_id(tuner_i2c)); - dev->dvb->frontend = dvb_attach(mb86a20s_attach, + dev->dvb->frontend[0] = dvb_attach(mb86a20s_attach, &pv_mb86a20s_config, demod_i2c); - if (dev->dvb->frontend == NULL) { + if (!dev->dvb->frontend[0]) { dev_err(dev->dev, "Failed to attach mb86a20s demod\n"); result = -EINVAL; @@ -952,9 +982,9 @@ static int dvb_init(struct cx231xx *dev) } /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; - dvb_attach(tda18271_attach, dev->dvb->frontend, + dvb_attach(tda18271_attach, dev->dvb->frontend[0], 0x60, tuner_i2c, &pv_tda18271_config); break; @@ -969,7 +999,7 @@ static int dvb_init(struct cx231xx *dev) /* attach demodulator chip */ si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */ - si2168_config.fe = &dev->dvb->frontend; + si2168_config.fe = &dev->dvb->frontend[0]; si2168_config.i2c_adapter = &adapter; si2168_config.ts_clock_inv = true; @@ -991,10 +1021,10 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dvb->i2c_client_demod = client; + dvb->i2c_client_demod[0] = client; /* attach tuner chip */ - si2157_config.fe = dev->dvb->frontend; + si2157_config.fe = dev->dvb->frontend[0]; #ifdef CONFIG_MEDIA_CONTROLLER_DVB si2157_config.mdev = dev->media_dev; #endif @@ -1010,16 +1040,16 @@ static int dvb_init(struct cx231xx *dev) client = i2c_new_device(tuner_i2c, &info); if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); result = -ENODEV; goto out_free; } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); result = -ENODEV; goto out_free; } @@ -1037,7 +1067,7 @@ static int dvb_init(struct cx231xx *dev) /* attach demodulator chip */ mn88473_config.i2c_wr_max = 16; mn88473_config.xtal = 25000000; - mn88473_config.fe = &dev->dvb->frontend; + mn88473_config.fe = &dev->dvb->frontend[0]; strlcpy(info.type, "mn88473", sizeof(info.type)); info.addr = dev->board.demod_addr; @@ -1057,24 +1087,219 @@ static int dvb_init(struct cx231xx *dev) goto out_free; } - dvb->i2c_client_demod = client; + dvb->i2c_client_demod[0] = client; /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; + dvb->frontend[0]->callback = cx231xx_tuner_callback; /* attach tuner chip */ - dvb_attach(r820t_attach, dev->dvb->frontend, + dvb_attach(r820t_attach, dev->dvb->frontend[0], tuner_i2c, &astrometa_t2hybrid_r820t_config); break; } + case CX231XX_BOARD_HAUPPAUGE_935C: + { + struct i2c_client *client; + struct i2c_adapter *adapter; + struct i2c_board_info info = {}; + struct si2157_config si2157_config = {}; + struct si2168_config si2168_config = {}; + + /* attach demodulator chip */ + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.fe = &dev->dvb->frontend[0]; + si2168_config.i2c_adapter = &adapter; + si2168_config.ts_clock_inv = true; + + strlcpy(info.type, "si2168", sizeof(info.type)); + info.addr = dev->board.demod_addr; + info.platform_data = &si2168_config; + + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + dev_err(dev->dev, + "Failed to attach %s frontend.\n", info.type); + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod[0] = client; + dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; + + /* define general-purpose callback pointer */ + dvb->frontend[0]->callback = cx231xx_tuner_callback; + + /* attach tuner */ + si2157_config.fe = dev->dvb->frontend[0]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 1; + si2157_config.inversion = true; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = dev->board.tuner_addr; + info.platform_data = &si2157_config; + request_module("si2157"); + + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + dev_err(dev->dev, + "Failed to obtain %s tuner.\n", info.type); + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + dev->cx231xx_reset_analog_tuner = NULL; + dev->dvb->i2c_client_tuner = client; + break; + } + case CX231XX_BOARD_HAUPPAUGE_975: + { + struct i2c_client *client; + struct i2c_adapter *adapter; + struct i2c_adapter *adapter2; + struct i2c_board_info info = {}; + struct si2157_config si2157_config = {}; + struct lgdt3306a_config lgdt3306a_config = {}; + struct si2168_config si2168_config = {}; + + /* attach first demodulator chip */ + lgdt3306a_config = hauppauge_955q_lgdt3306a_config; + lgdt3306a_config.fe = &dev->dvb->frontend[0]; + lgdt3306a_config.i2c_adapter = &adapter; + lgdt3306a_config.deny_i2c_rptr = 0; + + strlcpy(info.type, "lgdt3306a", sizeof(info.type)); + info.addr = dev->board.demod_addr; + info.platform_data = &lgdt3306a_config; + + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + dev_err(dev->dev, + "Failed to attach %s frontend.\n", info.type); + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod[0] = client; + + /* attach second demodulator chip */ + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.fe = &dev->dvb->frontend[1]; + si2168_config.i2c_adapter = &adapter2; + si2168_config.ts_clock_inv = true; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", sizeof(info.type)); + info.addr = dev->board.demod_addr2; + info.platform_data = &si2168_config; + + request_module(info.type); + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + dev_err(dev->dev, + "Failed to attach %s frontend.\n", info.type); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod[1] = client; + dvb->frontend[1]->id = 1; + + /* define general-purpose callback pointer */ + dvb->frontend[0]->callback = cx231xx_tuner_callback; + dvb->frontend[1]->callback = cx231xx_tuner_callback; + + /* attach tuner */ + si2157_config.fe = dev->dvb->frontend[0]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + si2157_config.if_port = 1; + si2157_config.inversion = true; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = dev->board.tuner_addr; + info.platform_data = &si2157_config; + request_module("si2157"); + + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod[1]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[1]); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + dev_err(dev->dev, + "Failed to obtain %s tuner.\n", info.type); + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod[1]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[1]); + module_put(dvb->i2c_client_demod[0]->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod[0]); + result = -ENODEV; + goto out_free; + } + + dev->cx231xx_reset_analog_tuner = NULL; + dvb->i2c_client_tuner = client; + + dvb->frontend[1]->tuner_priv = dvb->frontend[0]->tuner_priv; + + memcpy(&dvb->frontend[1]->ops.tuner_ops, + &dvb->frontend[0]->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + break; + } default: dev_err(dev->dev, "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", dev->name); break; } - if (NULL == dvb->frontend) { + if (!dvb->frontend[0]) { dev_err(dev->dev, "%s/2: frontend initialization failed\n", dev->name); result = -EINVAL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 5b321b8ada3a..f7fcd733a2ca 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1941,7 +1941,7 @@ static int cx231xx_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - call_all(dev, core, s_power, 0); + call_all(dev, tuner, standby); /* do this before setting alternate! */ if (dev->USE_ISO) diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 65b039cf80be..6ffa4bd96484 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -81,6 +81,8 @@ #define CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD 23 #define CX231XX_BOARD_ASTROMETA_T2HYBRID 24 #define CX231XX_BOARD_THE_IMAGING_SOURCE_DFG_USB2_PRO 25 +#define CX231XX_BOARD_HAUPPAUGE_935C 26 +#define CX231XX_BOARD_HAUPPAUGE_975 27 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 @@ -343,6 +345,7 @@ struct cx231xx_board { /* demod related */ int demod_addr; + int demod_addr2; u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */ /* GPIO Pins */ diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 0e4944b2b0f4..37053477b84d 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -15,7 +15,8 @@ config DVB_USB_V2 config DVB_USB_AF9015 tristate "Afatech AF9015 DVB-T USB2.0 support" - depends on DVB_USB_V2 + depends on DVB_USB_V2 && I2C_MUX + select REGMAP select DVB_AF9013 select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 8013659c41b1..39f9ffce3caa 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -29,6 +29,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) #define REQ_HDR_LEN 8 /* send header size */ #define ACK_HDR_LEN 2 /* rece header size */ struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, wlen, rlen; u8 write = 1; @@ -66,23 +67,24 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) case BOOT: break; default: - dev_err(&d->udev->dev, "%s: unknown command=%d\n", - KBUILD_MODNAME, req->cmd); + dev_err(&intf->dev, "unknown cmd %d\n", req->cmd); ret = -EIO; goto error; } - /* buffer overflow check */ + /* Buffer overflow check */ if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || - (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { - dev_err(&d->udev->dev, "%s: too much data; cmd=%d len=%d\n", - KBUILD_MODNAME, req->cmd, req->data_len); + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + dev_err(&intf->dev, "too much data, cmd %u, len %u\n", + req->cmd, req->data_len); ret = -EINVAL; goto error; } - /* write receives seq + status = 2 bytes - read receives seq + status + data = 2 + N bytes */ + /* + * Write receives seq + status = 2 bytes + * Read receives seq + status + data = 2 + N bytes + */ wlen = REQ_HDR_LEN; rlen = ACK_HDR_LEN; if (write) { @@ -96,15 +98,14 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) rlen = 0; - ret = dvb_usbv2_generic_rw_locked(d, - state->buf, wlen, state->buf, rlen); + ret = dvb_usbv2_generic_rw_locked(d, state->buf, wlen, + state->buf, rlen); if (ret) goto error; /* check status */ if (rlen && state->buf[1]) { - dev_err(&d->udev->dev, "%s: command failed=%d\n", - KBUILD_MODNAME, state->buf[1]); + dev_err(&intf->dev, "cmd failed %u\n", state->buf[1]); ret = -EIO; goto error; } @@ -118,121 +119,66 @@ error: return ret; } -static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val, - u8 len) -{ - struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, - val}; - return af9015_ctrl_msg(d, &req); -} - -static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len) -{ - struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, - val}; - return af9015_ctrl_msg(d, &req); -} - -static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val) -{ - return af9015_write_regs(d, addr, &val, 1); -} - -static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val) -{ - return af9015_read_regs(d, addr, val, 1); -} - static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, - u8 val) + u8 val) { struct af9015_state *state = d_to_priv(d); struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val}; - if (addr == state->af9013_config[0].i2c_addr || - addr == state->af9013_config[1].i2c_addr) + if (addr == state->af9013_i2c_addr[0] || + addr == state->af9013_i2c_addr[1]) req.addr_len = 3; return af9015_ctrl_msg(d, &req); } static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, - u8 *val) + u8 *val) { struct af9015_state *state = d_to_priv(d); struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val}; - if (addr == state->af9013_config[0].i2c_addr || - addr == state->af9013_config[1].i2c_addr) + if (addr == state->af9013_i2c_addr[0] || + addr == state->af9013_i2c_addr[1]) req.addr_len = 3; return af9015_ctrl_msg(d, &req); } -static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) -{ - int ret; - u8 val, mask = 0x01; - - ret = af9015_read_reg(d, addr, &val); - if (ret) - return ret; - - mask <<= bit; - if (op) { - /* set bit */ - val |= mask; - } else { - /* clear bit */ - mask ^= 0xff; - val &= mask; - } - - return af9015_write_reg(d, addr, val); -} - -static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) -{ - return af9015_do_reg_bit(d, addr, bit, 1); -} - -static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) -{ - return af9015_do_reg_bit(d, addr, bit, 0); -} - static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], - int num) + int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u16 addr; u8 mbox, addr_len; struct req_t req; -/* -The bus lock is needed because there is two tuners both using same I2C-address. -Due to that the only way to select correct tuner is use demodulator I2C-gate. - -................................................ -. AF9015 includes integrated AF9013 demodulator. -. ____________ ____________ . ____________ -.| uC | | demod | . | tuner | -.|------------| |------------| . |------------| -.| AF9015 | | AF9013/5 | . | MXL5003 | -.| |--+----I2C-------|-----/ -----|-.-----I2C-------| | -.| | | | addr 0x38 | . | addr 0xc6 | -.|____________| | |____________| . |____________| -.................|.............................. - | ____________ ____________ - | | demod | | tuner | - | |------------| |------------| - | | AF9013 | | MXL5003 | - +----I2C-------|-----/ -----|-------I2C-------| | - | addr 0x3a | | addr 0xc6 | - |____________| |____________| -*/ + /* + * I2C multiplexing: + * There could be two tuners, both using same I2C address. Demodulator + * I2C-gate is only possibility to select correct tuner. + * + * ........................................... + * . AF9015 integrates AF9013 demodulator . + * . ____________ ____________ . ____________ + * .| USB IF | | demod |. | tuner | + * .|------------| |------------|. |------------| + * .| AF9015 | | AF9013 |. | MXL5003 | + * .| |--+--I2C-----|-----/ -----|.----I2C-----| | + * .| | | | addr 0x1c |. | addr 0x63 | + * .|____________| | |____________|. |____________| + * .................|......................... + * | ____________ ____________ + * | | demod | | tuner | + * | |------------| |------------| + * | | AF9013 | | MXL5003 | + * +--I2C-----|-----/ -----|-----I2C-----| | + * | addr 0x1d | | addr 0x63 | + * |____________| |____________| + */ if (msg[0].len == 0 || msg[0].flags & I2C_M_RD) { addr = 0x0000; @@ -243,11 +189,11 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. mbox = 0; addr_len = 1; } else if (msg[0].len == 2) { - addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0; + addr = msg[0].buf[0] << 8 | msg[0].buf[1] << 0; mbox = 0; addr_len = 2; } else { - addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0; + addr = msg[0].buf[0] << 8 | msg[0].buf[1] << 0; mbox = msg[0].buf[2]; addr_len = 3; } @@ -258,7 +204,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. ret = -EOPNOTSUPP; goto err; } - if (msg[0].addr == state->af9013_config[0].i2c_addr) + if (msg[0].addr == state->af9013_i2c_addr[0]) req.cmd = WRITE_MEMORY; else req.cmd = WRITE_I2C; @@ -266,7 +212,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. req.addr = addr; req.mbox = mbox; req.addr_len = addr_len; - req.data_len = msg[0].len-addr_len; + req.data_len = msg[0].len - addr_len; req.data = &msg[0].buf[addr_len]; ret = af9015_ctrl_msg(d, &req); } else if (num == 2 && !(msg[0].flags & I2C_M_RD) && @@ -276,7 +222,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. ret = -EOPNOTSUPP; goto err; } - if (msg[0].addr == state->af9013_config[0].i2c_addr) + if (msg[0].addr == state->af9013_i2c_addr[0]) req.cmd = READ_MEMORY; else req.cmd = READ_I2C; @@ -293,7 +239,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. ret = -EOPNOTSUPP; goto err; } - if (msg[0].addr == state->af9013_config[0].i2c_addr) { + if (msg[0].addr == state->af9013_i2c_addr[0]) { ret = -EINVAL; goto err; } @@ -307,15 +253,14 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. ret = af9015_ctrl_msg(d, &req); } else { ret = -EOPNOTSUPP; - dev_dbg(&d->udev->dev, "%s: unknown msg, num %u\n", - __func__, num); + dev_dbg(&intf->dev, "unknown msg, num %u\n", num); } if (ret) goto err; return num; err: - dev_dbg(&d->udev->dev, "%s: failed %d\n", __func__, ret); + dev_dbg(&intf->dev, "failed %d\n", ret); return ret; } @@ -331,6 +276,7 @@ static struct i2c_algorithm af9015_i2c_algo = { static int af9015_identify_state(struct dvb_usb_device *d, const char **name) { + struct usb_interface *intf = d->intf; int ret; u8 reply; struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply}; @@ -339,7 +285,7 @@ static int af9015_identify_state(struct dvb_usb_device *d, const char **name) if (ret) return ret; - dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply); + dev_dbg(&intf->dev, "reply %02x\n", reply); if (reply == 0x02) ret = WARM; @@ -350,52 +296,47 @@ static int af9015_identify_state(struct dvb_usb_device *d, const char **name) } static int af9015_download_firmware(struct dvb_usb_device *d, - const struct firmware *fw) + const struct firmware *firmware) { struct af9015_state *state = d_to_priv(d); - int i, len, remaining, ret; + struct usb_interface *intf = d->intf; + int ret, i, rem; struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; - u16 checksum = 0; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + u16 checksum; - /* calc checksum */ - for (i = 0; i < fw->size; i++) - checksum += fw->data[i]; + dev_dbg(&intf->dev, "\n"); - state->firmware_size = fw->size; - state->firmware_checksum = checksum; + /* Calc checksum, we need it when copy firmware to slave demod */ + for (i = 0, checksum = 0; i < firmware->size; i++) + checksum += firmware->data[i]; - #define FW_ADDR 0x5100 /* firmware start address */ - #define LEN_MAX 55 /* max packet size */ - for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { - len = remaining; - if (len > LEN_MAX) - len = LEN_MAX; - - req.data_len = len; - req.data = (u8 *) &fw->data[fw->size - remaining]; - req.addr = FW_ADDR + fw->size - remaining; + state->firmware_size = firmware->size; + state->firmware_checksum = checksum; + #define LEN_MAX (BUF_LEN - REQ_HDR_LEN) /* Max payload size */ + for (rem = firmware->size; rem > 0; rem -= LEN_MAX) { + req.data_len = min(LEN_MAX, rem); + req.data = (u8 *)&firmware->data[firmware->size - rem]; + req.addr = 0x5100 + firmware->size - rem; ret = af9015_ctrl_msg(d, &req); if (ret) { - dev_err(&d->udev->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); - goto error; + dev_err(&intf->dev, "firmware download failed %d\n", + ret); + goto err; } } - /* firmware loaded, request boot */ req.cmd = BOOT; req.data_len = 0; ret = af9015_ctrl_msg(d, &req); if (ret) { - dev_err(&d->udev->dev, "%s: firmware boot failed=%d\n", - KBUILD_MODNAME, ret); - goto error; + dev_err(&intf->dev, "firmware boot failed %d\n", ret); + goto err; } -error: + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); return ret; } @@ -407,6 +348,7 @@ error: static int af9015_eeprom_hash(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, i; u8 buf[AF9015_EEPROM_SIZE]; struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, NULL}; @@ -427,24 +369,24 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d) } for (i = 0; i < AF9015_EEPROM_SIZE; i += 16) - dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, buf + i); + dev_dbg(&intf->dev, "%*ph\n", 16, buf + i); - dev_dbg(&d->udev->dev, "%s: eeprom sum=%.8x\n", - __func__, state->eeprom_sum); + dev_dbg(&intf->dev, "eeprom sum %.8x\n", state->eeprom_sum); return 0; err: - dev_err(&d->udev->dev, "%s: eeprom failed=%d\n", KBUILD_MODNAME, ret); + dev_dbg(&intf->dev, "failed %d\n", ret); return ret; } static int af9015_read_config(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 val, i, offset = 0; struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&intf->dev, "\n"); /* IR remote controller */ req.addr = AF9015_EEPROM_IR_MODE; @@ -462,7 +404,7 @@ static int af9015_read_config(struct dvb_usb_device *d) goto error; state->ir_mode = val; - dev_dbg(&d->udev->dev, "%s: IR mode=%d\n", __func__, val); + dev_dbg(&intf->dev, "ir mode %02x\n", val); /* TS mode - one or two receivers */ req.addr = AF9015_EEPROM_TS_MODE; @@ -471,13 +413,9 @@ static int af9015_read_config(struct dvb_usb_device *d) goto error; state->dual_mode = val; - dev_dbg(&d->udev->dev, "%s: TS mode=%d\n", __func__, state->dual_mode); + dev_dbg(&intf->dev, "ts mode %02x\n", state->dual_mode); - /* disable 2nd adapter because we don't have PID-filters */ - if (d->udev->speed == USB_SPEED_FULL) - state->dual_mode = 0; - - state->af9013_config[0].i2c_addr = AF9015_I2C_DEMOD; + state->af9013_i2c_addr[0] = AF9015_I2C_DEMOD; if (state->dual_mode) { /* read 2nd demodulator I2C address */ @@ -486,7 +424,7 @@ static int af9015_read_config(struct dvb_usb_device *d) if (ret) goto error; - state->af9013_config[1].i2c_addr = val >> 1; + state->af9013_i2c_addr[1] = val >> 1; } for (i = 0; i < state->dual_mode + 1; i++) { @@ -499,21 +437,20 @@ static int af9015_read_config(struct dvb_usb_device *d) goto error; switch (val) { case 0: - state->af9013_config[i].clock = 28800000; + state->af9013_pdata[i].clk = 28800000; break; case 1: - state->af9013_config[i].clock = 20480000; + state->af9013_pdata[i].clk = 20480000; break; case 2: - state->af9013_config[i].clock = 28000000; + state->af9013_pdata[i].clk = 28000000; break; case 3: - state->af9013_config[i].clock = 25000000; + state->af9013_pdata[i].clk = 25000000; break; } - dev_dbg(&d->udev->dev, "%s: [%d] xtal=%d set clock=%d\n", - __func__, i, val, - state->af9013_config[i].clock); + dev_dbg(&intf->dev, "[%d] xtal %02x, clk %u\n", + i, val, state->af9013_pdata[i].clk); /* IF frequency */ req.addr = AF9015_EEPROM_IF1H + offset; @@ -521,17 +458,17 @@ static int af9015_read_config(struct dvb_usb_device *d) if (ret) goto error; - state->af9013_config[i].if_frequency = val << 8; + state->af9013_pdata[i].if_frequency = val << 8; req.addr = AF9015_EEPROM_IF1L + offset; ret = af9015_ctrl_msg(d, &req); if (ret) goto error; - state->af9013_config[i].if_frequency += val; - state->af9013_config[i].if_frequency *= 1000; - dev_dbg(&d->udev->dev, "%s: [%d] IF frequency=%d\n", __func__, - i, state->af9013_config[i].if_frequency); + state->af9013_pdata[i].if_frequency += val; + state->af9013_pdata[i].if_frequency *= 1000; + dev_dbg(&intf->dev, "[%d] if frequency %u\n", + i, state->af9013_pdata[i].if_frequency); /* MT2060 IF1 */ req.addr = AF9015_EEPROM_MT2060_IF1H + offset; @@ -544,8 +481,8 @@ static int af9015_read_config(struct dvb_usb_device *d) if (ret) goto error; state->mt2060_if1[i] += val; - dev_dbg(&d->udev->dev, "%s: [%d] MT2060 IF1=%d\n", __func__, i, - state->mt2060_if1[i]); + dev_dbg(&intf->dev, "[%d] MT2060 IF1 %u\n", + i, state->mt2060_if1[i]); /* tuner */ req.addr = AF9015_EEPROM_TUNER_ID1 + offset; @@ -561,71 +498,177 @@ static int af9015_read_config(struct dvb_usb_device *d) case AF9013_TUNER_TDA18271: case AF9013_TUNER_QT1010A: case AF9013_TUNER_TDA18218: - state->af9013_config[i].spec_inv = 1; + state->af9013_pdata[i].spec_inv = 1; break; case AF9013_TUNER_MXL5003D: case AF9013_TUNER_MXL5005D: case AF9013_TUNER_MXL5005R: case AF9013_TUNER_MXL5007T: - state->af9013_config[i].spec_inv = 0; + state->af9013_pdata[i].spec_inv = 0; break; case AF9013_TUNER_MC44S803: - state->af9013_config[i].gpio[1] = AF9013_GPIO_LO; - state->af9013_config[i].spec_inv = 1; + state->af9013_pdata[i].gpio[1] = AF9013_GPIO_LO; + state->af9013_pdata[i].spec_inv = 1; break; default: - dev_err(&d->udev->dev, "%s: tuner id=%d not " \ - "supported, please report!\n", - KBUILD_MODNAME, val); + dev_err(&intf->dev, + "tuner id %02x not supported, please report!\n", + val); return -ENODEV; } - state->af9013_config[i].tuner = val; - dev_dbg(&d->udev->dev, "%s: [%d] tuner id=%d\n", - __func__, i, val); + state->af9013_pdata[i].tuner = val; + dev_dbg(&intf->dev, "[%d] tuner id %02x\n", i, val); } error: if (ret) - dev_err(&d->udev->dev, "%s: eeprom read failed=%d\n", - KBUILD_MODNAME, ret); + dev_err(&intf->dev, "eeprom read failed %d\n", ret); - /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM - content :-( Override some wrong values here. Ditto for the - AVerTV Red HD+ (A850T) device. */ + /* + * AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM + * content :-( Override some wrong values here. Ditto for the + * AVerTV Red HD+ (A850T) device. + */ if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA && - ((le16_to_cpu(d->udev->descriptor.idProduct) == - USB_PID_AVERMEDIA_A850) || - (le16_to_cpu(d->udev->descriptor.idProduct) == - USB_PID_AVERMEDIA_A850T))) { - dev_dbg(&d->udev->dev, - "%s: AverMedia A850: overriding config\n", - __func__); + ((le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850) || + (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850T))) { + dev_dbg(&intf->dev, "AverMedia A850: overriding config\n"); /* disable dual mode */ state->dual_mode = 0; /* set correct IF */ - state->af9013_config[0].if_frequency = 4570000; + state->af9013_pdata[0].if_frequency = 4570000; } return ret; } static int af9015_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, - struct usb_data_stream_properties *stream) + struct usb_data_stream_properties *stream) { struct dvb_usb_device *d = fe_to_d(fe); - dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id); + struct usb_interface *intf = d->intf; + + dev_dbg(&intf->dev, "adap %u\n", fe_to_adap(fe)->id); if (d->udev->speed == USB_SPEED_FULL) - stream->u.bulk.buffersize = TS_USB11_FRAME_SIZE; + stream->u.bulk.buffersize = 5 * 188; + + return 0; +} + +static int af9015_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; + int ret; + unsigned int utmp1, utmp2, reg1, reg2; + u8 buf[2]; + const unsigned int adap_id = fe_to_adap(fe)->id; + + dev_dbg(&intf->dev, "adap id %d, onoff %d\n", adap_id, onoff); + + if (!state->usb_ts_if_configured[adap_id]) { + dev_dbg(&intf->dev, "set usb and ts interface\n"); + + /* USB IF stream settings */ + utmp1 = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4; + utmp2 = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; + + buf[0] = (utmp1 >> 0) & 0xff; + buf[1] = (utmp1 >> 8) & 0xff; + if (adap_id == 0) { + /* 1st USB IF (EP4) stream settings */ + reg1 = 0xdd88; + reg2 = 0xdd0c; + } else { + /* 2nd USB IF (EP5) stream settings */ + reg1 = 0xdd8a; + reg2 = 0xdd0d; + } + ret = regmap_bulk_write(state->regmap, reg1, buf, 2); + if (ret) + goto err; + ret = regmap_write(state->regmap, reg2, utmp2); + if (ret) + goto err; + + /* TS IF settings */ + if (state->dual_mode) { + utmp1 = 0x01; + utmp2 = 0x10; + } else { + utmp1 = 0x00; + utmp2 = 0x00; + } + ret = regmap_update_bits(state->regmap, 0xd50b, 0x01, utmp1); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xd520, 0x10, utmp2); + if (ret) + goto err; + + state->usb_ts_if_configured[adap_id] = true; + } + + if (adap_id == 0 && onoff) { + /* Adapter 0 stream on. EP4: clear NAK, enable, clear reset */ + ret = regmap_update_bits(state->regmap, 0xdd13, 0x20, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd11, 0x20, 0x20); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xd507, 0x04, 0x00); + if (ret) + goto err; + } else if (adap_id == 1 && onoff) { + /* Adapter 1 stream on. EP5: clear NAK, enable, clear reset */ + ret = regmap_update_bits(state->regmap, 0xdd13, 0x40, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd11, 0x40, 0x40); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xd50b, 0x02, 0x00); + if (ret) + goto err; + } else if (adap_id == 0 && !onoff) { + /* Adapter 0 stream off. EP4: set reset, disable, set NAK */ + ret = regmap_update_bits(state->regmap, 0xd507, 0x04, 0x04); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd11, 0x20, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd13, 0x20, 0x20); + if (ret) + goto err; + } else if (adap_id == 1 && !onoff) { + /* Adapter 1 stream off. EP5: set reset, disable, set NAK */ + ret = regmap_update_bits(state->regmap, 0xd50b, 0x02, 0x02); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd11, 0x40, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(state->regmap, 0xdd13, 0x40, 0x40); + if (ret) + goto err; + } return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; } static int af9015_get_adapter_count(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + return state->dual_mode + 1; } @@ -647,7 +690,7 @@ static int af9015_af9013_set_frontend(struct dvb_frontend *fe) /* override demod callbacks for resource locking */ static int af9015_af9013_read_status(struct dvb_frontend *fe, - enum fe_status *status) + enum fe_status *status) { int ret; struct af9015_state *state = fe_to_priv(fe); @@ -729,102 +772,105 @@ static int af9015_tuner_sleep(struct dvb_frontend *fe) static int af9015_copy_firmware(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; - u8 fw_params[4]; - u8 val, i; - struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), - fw_params }; - dev_dbg(&d->udev->dev, "%s:\n", __func__); - - fw_params[0] = state->firmware_size >> 8; - fw_params[1] = state->firmware_size & 0xff; - fw_params[2] = state->firmware_checksum >> 8; - fw_params[3] = state->firmware_checksum & 0xff; - - ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr, - 0x98be, &val); - if (ret) - goto error; - else - dev_dbg(&d->udev->dev, "%s: firmware status=%02x\n", - __func__, val); + unsigned long timeout; + u8 val, firmware_info[4]; + struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, 4, firmware_info}; - if (val == 0x0c) /* fw is running, no need for download */ - goto exit; + dev_dbg(&intf->dev, "\n"); - /* set I2C master clock to fast (to speed up firmware copy) */ - ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */ + firmware_info[0] = (state->firmware_size >> 8) & 0xff; + firmware_info[1] = (state->firmware_size >> 0) & 0xff; + firmware_info[2] = (state->firmware_checksum >> 8) & 0xff; + firmware_info[3] = (state->firmware_checksum >> 0) & 0xff; + + /* Check whether firmware is already running */ + ret = af9015_read_reg_i2c(d, state->af9013_i2c_addr[1], 0x98be, &val); if (ret) - goto error; + goto err; - msleep(50); + dev_dbg(&intf->dev, "firmware status %02x\n", val); - /* copy firmware */ - ret = af9015_ctrl_msg(d, &req); + if (val == 0x0c) + return 0; + + /* Set i2c clock to 625kHz to speed up firmware copy */ + ret = regmap_write(state->regmap, 0xd416, 0x04); if (ret) - dev_err(&d->udev->dev, "%s: firmware copy cmd failed=%d\n", - KBUILD_MODNAME, ret); + goto err; - dev_dbg(&d->udev->dev, "%s: firmware copy done\n", __func__); + /* Copy firmware from master demod to slave demod */ + ret = af9015_ctrl_msg(d, &req); + if (ret) { + dev_err(&intf->dev, "firmware copy cmd failed %d\n", ret); + goto err; + } - /* set I2C master clock back to normal */ - ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ + /* Set i2c clock to 125kHz */ + ret = regmap_write(state->regmap, 0xd416, 0x14); if (ret) - goto error; + goto err; - /* request boot firmware */ - ret = af9015_write_reg_i2c(d, state->af9013_config[1].i2c_addr, - 0xe205, 1); - dev_dbg(&d->udev->dev, "%s: firmware boot cmd status=%d\n", - __func__, ret); + /* Boot firmware */ + ret = af9015_write_reg_i2c(d, state->af9013_i2c_addr[1], 0xe205, 0x01); if (ret) - goto error; + goto err; - for (i = 0; i < 15; i++) { - msleep(100); + /* Poll firmware ready */ + for (val = 0x00, timeout = jiffies + msecs_to_jiffies(1000); + !time_after(jiffies, timeout) && val != 0x0c && val != 0x04;) { + msleep(20); - /* check firmware status */ - ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr, - 0x98be, &val); - dev_dbg(&d->udev->dev, "%s: firmware status cmd status=%d " \ - "firmware status=%02x\n", __func__, ret, val); + /* Check firmware status. 0c=OK, 04=fail */ + ret = af9015_read_reg_i2c(d, state->af9013_i2c_addr[1], + 0x98be, &val); if (ret) - goto error; + goto err; - if (val == 0x0c || val == 0x04) /* success or fail */ - break; + dev_dbg(&intf->dev, "firmware status %02x\n", val); } + dev_dbg(&intf->dev, "firmware boot took %u ms\n", + jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - 1000)); + if (val == 0x04) { - dev_err(&d->udev->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); - ret = -ETIMEDOUT; + ret = -ENODEV; + dev_err(&intf->dev, "firmware did not run\n"); + goto err; } else if (val != 0x0c) { - dev_err(&d->udev->dev, "%s: firmware boot timeout\n", - KBUILD_MODNAME); ret = -ETIMEDOUT; + dev_err(&intf->dev, "firmware boot timeout\n"); + goto err; } -error: -exit: + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); return ret; } static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) { - int ret; struct af9015_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; + struct i2c_client *client; + int ret; + + dev_dbg(&intf->dev, "adap id %u\n", adap->id); if (adap->id == 0) { - state->af9013_config[0].ts_mode = AF9013_TS_USB; - memcpy(state->af9013_config[0].api_version, "\x0\x1\x9\x0", 4); - state->af9013_config[0].gpio[0] = AF9013_GPIO_HI; - state->af9013_config[0].gpio[3] = AF9013_GPIO_TUNER_ON; + state->af9013_pdata[0].ts_mode = AF9013_TS_MODE_USB; + memcpy(state->af9013_pdata[0].api_version, "\x0\x1\x9\x0", 4); + state->af9013_pdata[0].gpio[0] = AF9013_GPIO_HI; + state->af9013_pdata[0].gpio[3] = AF9013_GPIO_TUNER_ON; } else if (adap->id == 1) { - state->af9013_config[1].ts_mode = AF9013_TS_SERIAL; - memcpy(state->af9013_config[1].api_version, "\x0\x1\x9\x0", 4); - state->af9013_config[1].gpio[0] = AF9013_GPIO_TUNER_ON; - state->af9013_config[1].gpio[1] = AF9013_GPIO_LO; + state->af9013_pdata[1].ts_mode = AF9013_TS_MODE_SERIAL; + state->af9013_pdata[1].ts_output_pin = 7; + memcpy(state->af9013_pdata[1].api_version, "\x0\x1\x9\x0", 4); + state->af9013_pdata[1].gpio[0] = AF9013_GPIO_TUNER_ON; + state->af9013_pdata[1].gpio[1] = AF9013_GPIO_LO; /* copy firmware to 2nd demodulator */ if (state->dual_mode) { @@ -833,21 +879,27 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) ret = af9015_copy_firmware(adap_to_d(adap)); if (ret) { - dev_err(&adap_to_d(adap)->udev->dev, - "%s: firmware copy to 2nd " \ - "frontend failed, will " \ - "disable it\n", KBUILD_MODNAME); + dev_err(&intf->dev, + "firmware copy to 2nd frontend failed, will disable it\n"); state->dual_mode = 0; - return -ENODEV; + goto err; } } else { - return -ENODEV; + ret = -ENODEV; + goto err; } } - /* attach demodulator */ - adap->fe[0] = dvb_attach(af9013_attach, - &state->af9013_config[adap->id], &adap_to_d(adap)->i2c_adap); + /* Add I2C demod */ + client = dvb_module_probe("af9013", NULL, &d->i2c_adap, + state->af9013_i2c_addr[adap->id], + &state->af9013_pdata[adap->id]); + if (!client) { + ret = -ENODEV; + goto err; + } + adap->fe[0] = state->af9013_pdata[adap->id].get_dvb_frontend(client); + state->demod_i2c_client[adap->id] = client; /* * AF9015 firmware does not like if it gets interrupted by I2C adapter @@ -857,24 +909,36 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) * those "critical" paths to keep AF9015 happy. */ if (adap->fe[0]) { - state->set_frontend[adap->id] = - adap->fe[0]->ops.set_frontend; - adap->fe[0]->ops.set_frontend = - af9015_af9013_set_frontend; - - state->read_status[adap->id] = - adap->fe[0]->ops.read_status; - adap->fe[0]->ops.read_status = - af9015_af9013_read_status; - + state->set_frontend[adap->id] = adap->fe[0]->ops.set_frontend; + adap->fe[0]->ops.set_frontend = af9015_af9013_set_frontend; + state->read_status[adap->id] = adap->fe[0]->ops.read_status; + adap->fe[0]->ops.read_status = af9015_af9013_read_status; state->init[adap->id] = adap->fe[0]->ops.init; adap->fe[0]->ops.init = af9015_af9013_init; - state->sleep[adap->id] = adap->fe[0]->ops.sleep; adap->fe[0]->ops.sleep = af9015_af9013_sleep; } - return adap->fe[0] == NULL ? -ENODEV : 0; + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static int af9015_frontend_detach(struct dvb_usb_adapter *adap) +{ + struct af9015_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; + struct i2c_client *client; + + dev_dbg(&intf->dev, "adap id %u\n", adap->id); + + /* Remove I2C demod */ + client = state->demod_i2c_client[adap->id]; + dvb_module_release(client); + + return 0; } static struct mt2060_config af9015_mt2060_config = { @@ -944,64 +1008,61 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap_to_d(adap); struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; + struct i2c_client *client; + struct i2c_adapter *adapter; int ret; - dev_dbg(&d->udev->dev, "%s:\n", __func__); - switch (state->af9013_config[adap->id].tuner) { + dev_dbg(&intf->dev, "adap id %u\n", adap->id); + + client = state->demod_i2c_client[adap->id]; + adapter = state->af9013_pdata[adap->id].get_i2c_adapter(client); + + switch (state->af9013_pdata[adap->id].tuner) { case AF9013_TUNER_MT2060: case AF9013_TUNER_MT2060_2: - ret = dvb_attach(mt2060_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, &af9015_mt2060_config, - state->mt2060_if1[adap->id]) - == NULL ? -ENODEV : 0; + ret = dvb_attach(mt2060_attach, adap->fe[0], adapter, + &af9015_mt2060_config, + state->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_QT1010: case AF9013_TUNER_QT1010A: - ret = dvb_attach(qt1010_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - &af9015_qt1010_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(qt1010_attach, adap->fe[0], adapter, + &af9015_qt1010_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18271: - ret = dvb_attach(tda18271_attach, adap->fe[0], 0x60, - &adap_to_d(adap)->i2c_adap, - &af9015_tda18271_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(tda18271_attach, adap->fe[0], 0x60, adapter, + &af9015_tda18271_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18218: - ret = dvb_attach(tda18218_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - &af9015_tda18218_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(tda18218_attach, adap->fe[0], adapter, + &af9015_tda18218_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5003D: - ret = dvb_attach(mxl5005s_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - &af9015_mxl5003_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(mxl5005s_attach, adap->fe[0], adapter, + &af9015_mxl5003_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5005D: case AF9013_TUNER_MXL5005R: - ret = dvb_attach(mxl5005s_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - &af9015_mxl5005_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(mxl5005s_attach, adap->fe[0], adapter, + &af9015_mxl5005_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_ENV77H11D5: - ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0x60, - &adap_to_d(adap)->i2c_adap, - DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; + ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0x60, adapter, + DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MC44S803: - ret = dvb_attach(mc44s803_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - &af9015_mc44s803_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(mc44s803_attach, adap->fe[0], adapter, + &af9015_mc44s803_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MXL5007T: - ret = dvb_attach(mxl5007t_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, - 0x60, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0; + ret = dvb_attach(mxl5007t_attach, adap->fe[0], adapter, + 0x60, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_UNKNOWN: default: - dev_err(&d->udev->dev, "%s: unknown tuner id=%d\n", - KBUILD_MODNAME, - state->af9013_config[adap->id].tuner); + dev_err(&intf->dev, "unknown tuner, tuner id %02x\n", + state->af9013_pdata[adap->id].tuner); ret = -ENODEV; } @@ -1022,136 +1083,27 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) { - struct dvb_usb_device *d = adap_to_d(adap); - int ret; - dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); - - if (onoff) - ret = af9015_set_reg_bit(d, 0xd503, 0); - else - ret = af9015_clear_reg_bit(d, 0xd503, 0); - - return ret; -} - -static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, - int onoff) -{ - struct dvb_usb_device *d = adap_to_d(adap); + struct af9015_state *state = adap_to_priv(adap); + struct af9013_platform_data *pdata = &state->af9013_pdata[adap->id]; int ret; - u8 idx; - dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n", - __func__, index, pid, onoff); - - ret = af9015_write_reg(d, 0xd505, (pid & 0xff)); - if (ret) - goto error; - ret = af9015_write_reg(d, 0xd506, (pid >> 8)); - if (ret) - goto error; - - idx = ((index & 0x1f) | (1 << 5)); - ret = af9015_write_reg(d, 0xd504, idx); + mutex_lock(&state->fe_mutex); + ret = pdata->pid_filter_ctrl(adap->fe[0], onoff); + mutex_unlock(&state->fe_mutex); -error: return ret; } -static int af9015_init_endpoint(struct dvb_usb_device *d) +static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, + u16 pid, int onoff) { - struct af9015_state *state = d_to_priv(d); + struct af9015_state *state = adap_to_priv(adap); + struct af9013_platform_data *pdata = &state->af9013_pdata[adap->id]; int ret; - u16 frame_size; - u8 packet_size; - dev_dbg(&d->udev->dev, "%s: USB speed=%d\n", __func__, d->udev->speed); - - if (d->udev->speed == USB_SPEED_FULL) { - frame_size = TS_USB11_FRAME_SIZE/4; - packet_size = TS_USB11_MAX_PACKET_SIZE/4; - } else { - frame_size = TS_USB20_FRAME_SIZE/4; - packet_size = TS_USB20_MAX_PACKET_SIZE/4; - } - - ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */ - if (ret) - goto error; - ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */ - if (ret) - goto error; - ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */ - if (ret) - goto error; - ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */ - if (ret) - goto error; - ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */ - if (ret) - goto error; - if (state->dual_mode) { - ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */ - if (ret) - goto error; - } - ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */ - if (ret) - goto error; - if (state->dual_mode) { - ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */ - if (ret) - goto error; - } - /* EP4 xfer length */ - ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff); - if (ret) - goto error; - ret = af9015_write_reg(d, 0xdd89, frame_size >> 8); - if (ret) - goto error; - /* EP5 xfer length */ - ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff); - if (ret) - goto error; - ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8); - if (ret) - goto error; - ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */ - if (ret) - goto error; - ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */ - if (ret) - goto error; - ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */ - if (ret) - goto error; - if (state->dual_mode) { - ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */ - if (ret) - goto error; - } - /* enable / disable mp2if2 */ - if (state->dual_mode) { - ret = af9015_set_reg_bit(d, 0xd50b, 0); - if (ret) - goto error; - ret = af9015_set_reg_bit(d, 0xd520, 4); - if (ret) - goto error; - } else { - ret = af9015_clear_reg_bit(d, 0xd50b, 0); - if (ret) - goto error; - ret = af9015_clear_reg_bit(d, 0xd520, 4); - if (ret) - goto error; - } - -error: - if (ret) - dev_err(&d->udev->dev, "%s: endpoint init failed=%d\n", - KBUILD_MODNAME, ret); + mutex_lock(&state->fe_mutex); + ret = pdata->pid_filter(adap->fe[0], index, pid, onoff); + mutex_unlock(&state->fe_mutex); return ret; } @@ -1159,17 +1111,15 @@ error: static int af9015_init(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dev_dbg(&intf->dev, "\n"); mutex_init(&state->fe_mutex); /* init RC canary */ - ret = af9015_write_reg(d, 0x98e9, 0xff); - if (ret) - goto error; - - ret = af9015_init_endpoint(d); + ret = regmap_write(state->regmap, 0x98e9, 0xff); if (ret) goto error; @@ -1184,7 +1134,7 @@ struct af9015_rc_setup { }; static char *af9015_rc_setup_match(unsigned int id, - const struct af9015_rc_setup *table) + const struct af9015_rc_setup *table) { for (; table->rc_codes; table++) if (table->id == id) @@ -1212,24 +1162,25 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = { static int af9015_rc_query(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 buf[17]; /* read registers needed to detect remote controller code */ - ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf)); + ret = regmap_bulk_read(state->regmap, 0x98d9, buf, sizeof(buf)); if (ret) goto error; /* If any of these are non-zero, assume invalid data */ if (buf[1] || buf[2] || buf[3]) { - dev_dbg(&d->udev->dev, "%s: invalid data\n", __func__); + dev_dbg(&intf->dev, "invalid data\n"); return ret; } /* Check for repeat of previous code */ if ((state->rc_repeat != buf[6] || buf[0]) && - !memcmp(&buf[12], state->rc_last, 4)) { - dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__); + !memcmp(&buf[12], state->rc_last, 4)) { + dev_dbg(&intf->dev, "key repeated\n"); rc_repeat(d->rc_dev); state->rc_repeat = buf[6]; return ret; @@ -1238,18 +1189,18 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* Only process key if canary killed */ if (buf[16] != 0xff && buf[0] != 0x01) { enum rc_proto proto; - dev_dbg(&d->udev->dev, "%s: key pressed %*ph\n", - __func__, 4, buf + 12); + + dev_dbg(&intf->dev, "key pressed %*ph\n", 4, buf + 12); /* Reset the canary */ - ret = af9015_write_reg(d, 0x98e9, 0xff); + ret = regmap_write(state->regmap, 0x98e9, 0xff); if (ret) goto error; /* Remember this key */ memcpy(state->rc_last, &buf[12], 4); - if (buf[14] == (u8) ~buf[15]) { - if (buf[12] == (u8) ~buf[13]) { + if (buf[14] == (u8)~buf[15]) { + if (buf[12] == (u8)~buf[13]) { /* NEC */ state->rc_keycode = RC_SCANCODE_NEC(buf[12], buf[14]); @@ -1271,7 +1222,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) } rc_keydown(d->rc_dev, proto, state->rc_keycode, 0); } else { - dev_dbg(&d->udev->dev, "%s: no key press\n", __func__); + dev_dbg(&intf->dev, "no key press\n"); /* Invalidate last keypress */ /* Not really needed, but helps with debug */ state->rc_last[2] = state->rc_last[3]; @@ -1282,8 +1233,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) error: if (ret) { - dev_warn(&d->udev->dev, "%s: rc query failed=%d\n", - KBUILD_MODNAME, ret); + dev_warn(&intf->dev, "rc query failed %d\n", ret); /* allow random errors as dvb-usb will stop polling on error */ if (!state->rc_failed) @@ -1306,29 +1256,33 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) /* try to load remote based module param */ if (!rc->map_name) rc->map_name = af9015_rc_setup_match(dvb_usb_af9015_remote, - af9015_rc_setup_modparam); + af9015_rc_setup_modparam); /* try to load remote based eeprom hash */ if (!rc->map_name) rc->map_name = af9015_rc_setup_match(state->eeprom_sum, - af9015_rc_setup_hashes); + af9015_rc_setup_hashes); /* try to load remote based USB iManufacturer string */ if (!rc->map_name && vid == USB_VID_AFATECH) { - /* Check USB manufacturer and product strings and try - to determine correct remote in case of chip vendor - reference IDs are used. - DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */ + /* + * Check USB manufacturer and product strings and try + * to determine correct remote in case of chip vendor + * reference IDs are used. + * DO NOT ADD ANYTHING NEW HERE. Use hashes instead. + */ char manufacturer[10]; + memset(manufacturer, 0, sizeof(manufacturer)); usb_string(d->udev, d->udev->descriptor.iManufacturer, - manufacturer, sizeof(manufacturer)); + manufacturer, sizeof(manufacturer)); if (!strcmp("MSI", manufacturer)) { - /* iManufacturer 1 MSI - iProduct 2 MSI K-VOX */ - rc->map_name = af9015_rc_setup_match( - AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, - af9015_rc_setup_modparam); + /* + * iManufacturer 1 MSI + * iProduct 2 MSI K-VOX + */ + rc->map_name = af9015_rc_setup_match(AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + af9015_rc_setup_modparam); } } @@ -1347,15 +1301,68 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) #define af9015_get_rc_config NULL #endif -static int af9015_probe(struct usb_interface *intf, - const struct usb_device_id *id) +static int af9015_regmap_write(void *context, const void *data, size_t count) { + struct dvb_usb_device *d = context; + struct usb_interface *intf = d->intf; + int ret; + u16 reg = ((u8 *)data)[0] << 8 | ((u8 *)data)[1] << 0; + u8 *val = &((u8 *)data)[2]; + const unsigned int len = count - 2; + struct req_t req = {WRITE_MEMORY, 0, reg, 0, 0, len, val}; + + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static int af9015_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct dvb_usb_device *d = context; + struct usb_interface *intf = d->intf; + int ret; + u16 reg = ((u8 *)reg_buf)[0] << 8 | ((u8 *)reg_buf)[1] << 0; + u8 *val = &((u8 *)val_buf)[0]; + const unsigned int len = val_size; + struct req_t req = {READ_MEMORY, 0, reg, 0, 0, len, val}; + + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static int af9015_probe(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; struct usb_device *udev = interface_to_usbdev(intf); + int ret; char manufacturer[sizeof("ITE Technologies, Inc.")]; + static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + }; + static const struct regmap_bus regmap_bus = { + .read = af9015_regmap_read, + .write = af9015_regmap_write, + }; + + dev_dbg(&intf->dev, "\n"); memset(manufacturer, 0, sizeof(manufacturer)); usb_string(udev, udev->descriptor.iManufacturer, - manufacturer, sizeof(manufacturer)); + manufacturer, sizeof(manufacturer)); /* * There is two devices having same ID but different chipset. One uses * AF9015 and the other IT9135 chipset. Only difference seen on lsusb @@ -1374,19 +1381,41 @@ static int af9015_probe(struct usb_interface *intf, * iProduct 2 DVB-T TV Stick */ if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) && - (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { + (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { if (!strcmp("ITE Technologies, Inc.", manufacturer)) { - dev_dbg(&udev->dev, "%s: rejecting device\n", __func__); - return -ENODEV; + ret = -ENODEV; + dev_dbg(&intf->dev, "rejecting device\n"); + goto err; } } - return dvb_usbv2_probe(intf, id); + state->regmap = regmap_init(&intf->dev, ®map_bus, d, ®map_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + goto err; + } + + return 0; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; } -/* interface 0 is used by DVB-T receiver and - interface 1 is for remote controller (HID) */ -static struct dvb_usb_device_properties af9015_props = { +static void af9015_disconnect(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; + + dev_dbg(&intf->dev, "\n"); + + regmap_exit(state->regmap); +} + +/* + * Interface 0 is used by DVB-T receiver and + * interface 1 is for remote controller (HID) + */ +static const struct dvb_usb_device_properties af9015_props = { .driver_name = KBUILD_MODNAME, .owner = THIS_MODULE, .adapter_nr = adapter_nr, @@ -1395,6 +1424,8 @@ static struct dvb_usb_device_properties af9015_props = { .generic_bulk_ctrl_endpoint = 0x02, .generic_bulk_ctrl_endpoint_response = 0x81, + .probe = af9015_probe, + .disconnect = af9015_disconnect, .identify_state = af9015_identify_state, .firmware = AF9015_FIRMWARE, .download_firmware = af9015_download_firmware, @@ -1402,10 +1433,12 @@ static struct dvb_usb_device_properties af9015_props = { .i2c_algo = &af9015_i2c_algo, .read_config = af9015_read_config, .frontend_attach = af9015_af9013_frontend_attach, + .frontend_detach = af9015_frontend_detach, .tuner_attach = af9015_tuner_attach, .init = af9015_init, .get_rc_config = af9015_get_rc_config, .get_stream_config = af9015_get_stream_config, + .streaming_ctrl = af9015_streaming_ctrl, .get_adapter_count = af9015_get_adapter_count, .adapter = { @@ -1416,9 +1449,15 @@ static struct dvb_usb_device_properties af9015_props = { .pid_filter = af9015_pid_filter, .pid_filter_ctrl = af9015_pid_filter_ctrl, - .stream = DVB_USB_STREAM_BULK(0x84, 8, TS_USB20_FRAME_SIZE), + .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), }, { - .stream = DVB_USB_STREAM_BULK(0x85, 8, TS_USB20_FRAME_SIZE), + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188), }, }, }; @@ -1509,7 +1548,7 @@ MODULE_DEVICE_TABLE(usb, af9015_id_table); static struct usb_driver af9015_usb_driver = { .name = KBUILD_MODNAME, .id_table = af9015_id_table, - .probe = af9015_probe, + .probe = dvb_usbv2_probe, .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h index 3a9d9815ab7a..ad2b045cc39c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.h +++ b/drivers/media/usb/dvb-usb-v2/af9015.h @@ -21,6 +21,7 @@ #define AF9015_H #include <linux/hash.h> +#include <linux/regmap.h> #include "dvb_usb.h" #include "af9013.h" #include "dvb-pll.h" @@ -34,19 +35,6 @@ #define AF9015_FIRMWARE "dvb-usb-af9015.fw" -/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. - We use smaller - about 1/4 from the original, 5 and 87. */ -#define TS_PACKET_SIZE 188 - -#define TS_USB20_PACKET_COUNT 87 -#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) - -#define TS_USB11_PACKET_COUNT 5 -#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) - -#define TS_USB20_MAX_PACKET_SIZE 512 -#define TS_USB11_MAX_PACKET_SIZE 64 - #define AF9015_I2C_EEPROM 0x50 #define AF9015_I2C_DEMOD 0x1c #define AF9015_USB_TIMEOUT 2000 @@ -113,6 +101,7 @@ enum af9015_ir_mode { #define BUF_LEN 63 struct af9015_state { + struct regmap *regmap; u8 buf[BUF_LEN]; /* bulk USB control message */ u8 ir_mode; u8 rc_repeat; @@ -125,7 +114,10 @@ struct af9015_state { u16 firmware_size; u16 firmware_checksum; u32 eeprom_sum; - struct af9013_config af9013_config[2]; + struct af9013_platform_data af9013_pdata[2]; + struct i2c_client *demod_i2c_client[2]; + u8 af9013_i2c_addr[2]; + bool usb_ts_if_configured[2]; /* for demod callback override */ int (*set_frontend[2]) (struct dvb_frontend *fe); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index d2e80537b2f7..3fd6cc0d6340 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -203,6 +203,8 @@ struct dvb_usb_adapter_properties { * @generic_bulk_ctrl_endpoint_response: bulk control endpoint number for * receive * @generic_bulk_ctrl_delay: delay between bulk control sent and receive message + * @probe: like probe on driver model + * @disconnect: like disconnect on driver model * @identify_state: called to determine the firmware state (cold or warm) and * return possible firmware file name to be loaded * @firmware: name of the firmware file to be loaded @@ -239,6 +241,8 @@ struct dvb_usb_device_properties { u8 generic_bulk_ctrl_endpoint_response; unsigned int generic_bulk_ctrl_delay; + int (*probe)(struct dvb_usb_device *); + void (*disconnect)(struct dvb_usb_device *); #define WARM 0 #define COLD 1 int (*identify_state) (struct dvb_usb_device *, const char **); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 2bf3bd81280a..afdcdbf005e9 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -854,8 +854,6 @@ static int dvb_usbv2_exit(struct dvb_usb_device *d) dvb_usbv2_remote_exit(d); dvb_usbv2_adapter_exit(d); dvb_usbv2_i2c_exit(d); - kfree(d->priv); - kfree(d); return 0; } @@ -934,7 +932,7 @@ int dvb_usbv2_probe(struct usb_interface *intf, if (intf->cur_altsetting->desc.bInterfaceNumber != d->props->bInterfaceNumber) { ret = -ENODEV; - goto err_free_all; + goto err_kfree_d; } mutex_init(&d->usb_mutex); @@ -946,10 +944,16 @@ int dvb_usbv2_probe(struct usb_interface *intf, dev_err(&d->udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); ret = -ENOMEM; - goto err_free_all; + goto err_kfree_d; } } + if (d->props->probe) { + ret = d->props->probe(d); + if (ret) + goto err_kfree_priv; + } + if (d->props->identify_state) { const char *name = NULL; ret = d->props->identify_state(d, &name); @@ -1001,6 +1005,12 @@ exit: return 0; err_free_all: dvb_usbv2_exit(d); + if (d->props->disconnect) + d->props->disconnect(d); +err_kfree_priv: + kfree(d->priv); +err_kfree_d: + kfree(d); err: dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -1021,6 +1031,12 @@ void dvb_usbv2_disconnect(struct usb_interface *intf) dvb_usbv2_exit(d); + if (d->props->disconnect) + d->props->disconnect(d); + + kfree(d->priv); + kfree(d); + pr_info("%s: '%s:%s' successfully deinitialized and disconnected\n", KBUILD_MODNAME, drvname, devname); kfree(devname); diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 2abd15c6df81..387a074ea6ec 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1243,82 +1243,6 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) return 0; } -static int cxusb_mygica_t230c_frontend_attach(struct dvb_usb_adapter *adap) -{ - struct dvb_usb_device *d = adap->dev; - struct cxusb_state *st = d->priv; - struct i2c_adapter *adapter; - struct i2c_client *client_demod; - struct i2c_client *client_tuner; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* Select required USB configuration */ - if (usb_set_interface(d->udev, 0, 0) < 0) - err("set interface failed"); - - /* Unblock all USB pipes */ - usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); - - /* attach frontend */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &adapter; - si2168_config.fe = &adap->fe_adap[0].fe; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - si2168_config.ts_clock_inv = 1; - 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(info.type); - client_demod = i2c_new_device(&d->i2c_adap, &info); - if (client_demod == NULL || client_demod->dev.driver == NULL) - return -ENODEV; - - if (!try_module_get(client_demod->dev.driver->owner)) { - i2c_unregister_device(client_demod); - return -ENODEV; - } - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = adap->fe_adap[0].fe; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2141", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - request_module("si2157"); - client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || client_tuner->dev.driver == NULL) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - return -ENODEV; - } - 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); - return -ENODEV; - } - - st->i2c_client_demod = client_demod; - st->i2c_client_tuner = client_tuner; - - /* hook fe: need to resync the slave fifo when signal locks. */ - mutex_init(&st->stream_mutex); - st->last_lock = 0; - st->fe_read_status = adap->fe_adap[0].fe->ops.read_status; - adap->fe_adap[0].fe->ops.read_status = cxusb_read_status; - - return 0; -} - /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1401,7 +1325,6 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static struct dvb_usb_device_properties cxusb_mygica_t230_properties; -static struct dvb_usb_device_properties cxusb_mygica_t230c_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1434,8 +1357,6 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230c_properties, - THIS_MODULE, NULL, adapter_nr) || 0) return 0; @@ -1487,7 +1408,6 @@ enum cxusb_table_index { CONEXANT_D680_DMB, MYGICA_D689, MYGICA_T230, - MYGICA_T230C, NR__cxusb_table_index }; @@ -1555,9 +1475,6 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { [MYGICA_T230] = { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230) }, - [MYGICA_T230C] = { - USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230+1) - }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -2143,7 +2060,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { .rc.core = { .rc_interval = 100, - .rc_codes = RC_MAP_D680_DMB, + .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02, .module_name = KBUILD_MODNAME, .rc_query = cxusb_d680_dmb_rc_query, .allowed_protos = RC_PROTO_BIT_UNKNOWN, @@ -2252,7 +2169,7 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = { .rc.core = { .rc_interval = 100, - .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02, + .rc_codes = RC_MAP_D680_DMB, .module_name = KBUILD_MODNAME, .rc_query = cxusb_d680_dmb_rc_query, .allowed_protos = RC_PROTO_BIT_UNKNOWN, @@ -2268,60 +2185,6 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = { } }; -static struct dvb_usb_device_properties cxusb_mygica_t230c_properties = { - .caps = DVB_USB_IS_AN_I2C_ADAPTER, - - .usb_ctrl = CYPRESS_FX2, - - .size_of_priv = sizeof(struct cxusb_state), - - .num_adapters = 1, - .adapter = { - { - .num_frontends = 1, - .fe = {{ - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mygica_t230c_frontend_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, - } }, - }, - }, - - .power_ctrl = cxusb_d680_dmb_power_ctrl, - - .i2c_algo = &cxusb_i2c_algo, - - .generic_bulk_ctrl_endpoint = 0x01, - - .rc.core = { - .rc_interval = 100, - .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02, - .module_name = KBUILD_MODNAME, - .rc_query = cxusb_d680_dmb_rc_query, - .allowed_protos = RC_PROTO_BIT_UNKNOWN, - }, - - .num_device_descs = 1, - .devices = { - { - "Mygica T230C DVB-T/T2/C", - { NULL }, - { &cxusb_table[MYGICA_T230C], NULL }, - }, - } -}; - static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 3d99e141d566..c53a969bc6be 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -3412,7 +3412,7 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) static struct s5h1411_config pinnacle_801e_config = { .output_mode = S5H1411_PARALLEL_OUTPUT, .gpio = S5H1411_GPIO_OFF, - .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, + .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK, .qam_if = S5H1411_IF_44000, .vsb_if = S5H1411_IF_44000, .inversion = S5H1411_INVERSION_OFF, diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 4628d73f46f2..8e799ae1df69 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -1,25 +1,25 @@ -/* - * Empiatech em28x1 audio extension - * - * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> - * - * Copyright (C) 2007-2016 Mauro Carvalho Chehab - * - Port to work with the in-kernel driver - * - Cleanups, fixes, alsa-controls, etc. - * - * This driver is based on my previous au600 usb pstn audio driver - * and inherits all the copyrights - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Empiatech em28x1 audio extension +// +// Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> +// +// Copyright (C) 2007-2016 Mauro Carvalho Chehab +// - Port to work with the in-kernel driver +// - Cleanups, fixes, alsa-controls, etc. +// +// This driver is based on my previous au600 usb pstn audio driver +// and inherits all the copyrights +// +// 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 "em28xx.h" @@ -103,7 +103,7 @@ static void em28xx_audio_isocirq(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dprintk("urb completition error %d.\n", urb->status); + dprintk("urb completion error %d.\n", urb->status); break; } @@ -165,12 +165,11 @@ static void em28xx_audio_isocirq(struct urb *urb) dev_err(&dev->intf->dev, "resubmit of audio urb failed (error=%i)\n", status); - return; } static int em28xx_init_audio_isoc(struct em28xx *dev) { - int i, errCode; + int i, err; dprintk("Starting isoc transfers\n"); @@ -179,16 +178,15 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) memset(dev->adev.transfer_buffer[i], 0x80, dev->adev.urb[i]->transfer_buffer_length); - errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); - if (errCode) { + err = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (err) { dev_err(&dev->intf->dev, "submit of audio urb failed (error=%i)\n", - errCode); + err); em28xx_deinit_isoc_audio(dev); atomic_set(&dev->adev.stream_started, 0); - return errCode; + return err; } - } return 0; @@ -268,14 +266,17 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) if (nonblock) { if (!mutex_trylock(&dev->lock)) return -EAGAIN; - } else + } else { mutex_lock(&dev->lock); + } runtime->hw = snd_em28xx_hw_capture; if (dev->adev.users == 0) { - if (dev->alt == 0 || dev->is_audio_only) { - struct usb_device *udev = interface_to_usbdev(dev->intf); + if (!dev->alt || dev->is_audio_only) { + struct usb_device *udev; + + udev = interface_to_usbdev(dev->intf); if (dev->is_audio_only) /* audio is on a separate interface */ @@ -367,9 +368,11 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; #if 0 - /* TODO: set up em28xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ + /* + * TODO: set up em28xx audio chip to deliver the correct audio format, + * current default is 48000hz multiplexed => 96000hz mono + * which shouldn't matter since analogue TV only supports mono + */ unsigned int channels, rate, format; format = params_format(hw_params); @@ -513,8 +516,9 @@ static int em28xx_vol_put(struct snd_kcontrol *kcontrol, if (nonblock) { if (!mutex_trylock(&dev->lock)) return -EAGAIN; - } else + } else { mutex_lock(&dev->lock); + } rc = em28xx_read_ac97(dev, kcontrol->private_value); if (rc < 0) goto err; @@ -551,8 +555,9 @@ static int em28xx_vol_get(struct snd_kcontrol *kcontrol, if (nonblock) { if (!mutex_trylock(&dev->lock)) return -EAGAIN; - } else + } else { mutex_lock(&dev->lock); + } val = em28xx_read_ac97(dev, kcontrol->private_value); mutex_unlock(&dev->lock); if (val < 0) @@ -586,8 +591,9 @@ static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol, if (nonblock) { if (!mutex_trylock(&dev->lock)) return -EAGAIN; - } else + } else { mutex_lock(&dev->lock); + } rc = em28xx_read_ac97(dev, kcontrol->private_value); if (rc < 0) goto err; @@ -627,8 +633,9 @@ static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol, if (nonblock) { if (!mutex_trylock(&dev->lock)) return -EAGAIN; - } else + } else { mutex_lock(&dev->lock); + } val = em28xx_read_ac97(dev, kcontrol->private_value); mutex_unlock(&dev->lock); if (val < 0) @@ -762,7 +769,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev) if (intf->num_altsetting <= alt) { dev_err(&dev->intf->dev, "alt %d doesn't exist on interface %d\n", - dev->ifnum, alt); + dev->ifnum, alt); return -ENODEV; } @@ -836,9 +843,8 @@ static int em28xx_audio_urb_init(struct em28xx *dev) dev->adev.transfer_buffer = kcalloc(num_urb, sizeof(*dev->adev.transfer_buffer), GFP_ATOMIC); - if (!dev->adev.transfer_buffer) { + if (!dev->adev.transfer_buffer) return -ENOMEM; - } dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_ATOMIC); if (!dev->adev.urb) { @@ -899,9 +905,11 @@ static int em28xx_audio_init(struct em28xx *dev) int err; if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ + /* + * This device does not support the extension (in this case + * the device is expecting the snd-usb-audio module or + * doesn't have analog audio support at all) + */ return 0; } @@ -977,13 +985,15 @@ card_free: static int em28xx_audio_fini(struct em28xx *dev) { - if (dev == NULL) + if (!dev) return 0; if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ + /* + * This device does not support the extension (in this case + * the device is expecting the snd-usb-audio module or + * doesn't have analog audio support at all) + */ return 0; } @@ -1005,7 +1015,7 @@ static int em28xx_audio_fini(struct em28xx *dev) static int em28xx_audio_suspend(struct em28xx *dev) { - if (dev == NULL) + if (!dev) return 0; if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) @@ -1019,7 +1029,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) static int em28xx_audio_resume(struct em28xx *dev) { - if (dev == NULL) + if (!dev) return 0; if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) @@ -1050,7 +1060,7 @@ static void __exit em28xx_alsa_unregister(void) em28xx_unregister_extension(&audio_ops); } -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_DESCRIPTION(DRIVER_DESC " - audio interface"); diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index ae87dd3e671f..3c2694a16ed1 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -1,23 +1,19 @@ -/* - em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices - - Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org> - Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices +// +// Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org> +// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// 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 "em28xx.h" @@ -49,7 +45,7 @@ static int em28xx_initialize_mt9m111(struct em28xx *dev) { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ { 0x0d, 0x00, 0x00, }, { 0x0a, 0x00, 0x21, }, - { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ + { 0x21, 0x04, 0x00, }, /* full readout spd, no row/col skip */ }; for (i = 0; i < ARRAY_SIZE(regs); i++) @@ -157,7 +153,8 @@ static int em28xx_probe_sensor_micron(struct em28xx *dev) break; default: dev_info(&dev->intf->dev, - "unknown Micron sensor detected: 0x%04x\n", id); + "unknown Micron sensor detected: 0x%04x\n", + id); return 0; } @@ -186,8 +183,10 @@ static int em28xx_probe_sensor_omnivision(struct em28xx *dev) struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus]; dev->em28xx_sensor = EM28XX_NOSENSOR; - /* NOTE: these devices have the register auto incrementation disabled - * by default, so we have to use single byte reads ! */ + /* + * NOTE: these devices have the register auto incrementation disabled + * by default, so we have to use single byte reads ! + */ for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) { client->addr = omnivision_sensor_addrs[i]; /* Read manufacturer ID from registers 0x1c-0x1d (BE) */ @@ -397,7 +396,7 @@ int em28xx_init_camera(struct em28xx *dev) subdev = v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, &ov2640_info, NULL); - if (subdev == NULL) + if (!subdev) return -ENODEV; format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 34e16f6ab4ac..6e0e67d23876 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -1,27 +1,23 @@ -/* - em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB - video capture devices - - Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> - Markus Rechberger <mrechberger@gmail.com> - Mauro Carvalho Chehab <mchehab@infradead.org> - Sascha Sommer <saschasommer@freenet.de> - Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB +// video capture devices +// +// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> +// Markus Rechberger <mrechberger@gmail.com> +// Mauro Carvalho Chehab <mchehab@infradead.org> +// Sascha Sommer <saschasommer@freenet.de> +// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// 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 "em28xx.h" @@ -40,7 +36,6 @@ #include <media/v4l2-common.h> #include <sound/ac97_codec.h> - #define DRIVER_NAME "em28xx" static int tuner = -1; @@ -81,26 +76,26 @@ static void em28xx_pre_card_setup(struct em28xx *dev); */ /* Reset for the most [analog] boards */ -static struct em28xx_reg_seq default_analog[] = { +static const struct em28xx_reg_seq default_analog[] = { {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; /* Reset for the most [digital] boards */ -static struct em28xx_reg_seq default_digital[] = { +static const struct em28xx_reg_seq default_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; /* Board Hauppauge WinTV HVR 900 analog */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { +static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { {EM2820_R08_GPIO_CTRL, 0x2d, ~EM_GPIO_4, 10}, { 0x05, 0xff, 0x10, 10}, { -1, -1, -1, -1}, }; /* Board Hauppauge WinTV HVR 900 digital */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { +static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { {EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x04, 0x0f, 10}, {EM2880_R04_GPO, 0x0c, 0x0f, 10}, @@ -108,25 +103,20 @@ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { }; /* Board Hauppauge WinTV HVR 900 (R2) digital */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = { +static const struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = { {EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x0c, 0x0f, 10}, { -1, -1, -1, -1}, }; /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { +static const struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { {EM2820_R08_GPIO_CTRL, 0x69, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ - -/* Board - EM2870 Kworld 355u - Analog - No input analog */ - /* Board - EM2882 Kworld 315U digital */ -static struct em28xx_reg_seq em2882_kworld_315u_digital[] = { +static const struct em28xx_reg_seq em2882_kworld_315u_digital[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10}, {EM2880_R04_GPO, 0x04, 0xff, 10}, @@ -135,7 +125,7 @@ static struct em28xx_reg_seq em2882_kworld_315u_digital[] = { { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { +static const struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { {EM2880_R04_GPO, 0x08, 0xff, 10}, {EM2880_R04_GPO, 0x0c, 0xff, 10}, {EM2880_R04_GPO, 0x08, 0xff, 10}, @@ -143,30 +133,31 @@ static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq kworld_330u_analog[] = { +static const struct em28xx_reg_seq kworld_330u_analog[] = { {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x00, 0xff, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq kworld_330u_digital[] = { +static const struct em28xx_reg_seq kworld_330u_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x08, 0xff, 10}, { -1, -1, -1, -1}, }; -/* Evga inDtube - GPIO0 - Enable digital power (s5h1409) - low to enable - GPIO1 - Enable analog power (tvp5150/emp202) - low to enable - GPIO4 - xc3028 reset - GOP3 - s5h1409 reset +/* + * Evga inDtube + * GPIO0 - Enable digital power (s5h1409) - low to enable + * GPIO1 - Enable analog power (tvp5150/emp202) - low to enable + * GPIO4 - xc3028 reset + * GOP3 - s5h1409 reset */ -static struct em28xx_reg_seq evga_indtube_analog[] = { +static const struct em28xx_reg_seq evga_indtube_analog[] = { {EM2820_R08_GPIO_CTRL, 0x79, 0xff, 60}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq evga_indtube_digital[] = { +static const struct em28xx_reg_seq evga_indtube_digital[] = { {EM2820_R08_GPIO_CTRL, 0x7a, 0xff, 1}, {EM2880_R04_GPO, 0x04, 0xff, 10}, {EM2880_R04_GPO, 0x0c, 0xff, 1}, @@ -184,12 +175,12 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { * EM_GPIO_6 - currently unknown * EM_GPIO_7 - currently unknown */ -static struct em28xx_reg_seq kworld_a340_digital[] = { +static const struct em28xx_reg_seq kworld_a340_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = { +static const struct em28xx_reg_seq kworld_ub435q_v3_digital[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xbe, 0xff, 100}, @@ -198,45 +189,49 @@ static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = { }; /* Pinnacle Hybrid Pro eb1a:2881 */ -static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { +static const struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { {EM2820_R08_GPIO_CTRL, 0xfd, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { +static const struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ {EM2880_R04_GPO, 0x0c, 0xff, 1}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = { +static const struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = { {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x00, 0xff, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = { +static const struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x08, 0xff, 10}, { -1, -1, -1, -1}, }; -/* PCTV HD Mini (80e) GPIOs - 0-5: not used - 6: demod reset, active low - 7: LED on, active high */ -static struct em28xx_reg_seq em2874_pctv_80e_digital[] = { +/* + * PCTV HD Mini (80e) GPIOs + * 0-5: not used + * 6: demod reset, active low + * 7: LED on, active high + */ +static const struct em28xx_reg_seq em2874_pctv_80e_digital[] = { {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ {EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 100},/*Demod reset*/ {EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 10}, { -1, -1, -1, -1}, }; -/* eb1a:2868 Reddo DVB-C USB TV Box - GPIO4 - CU1216L NIM - Other GPIOs seems to be don't care. */ -static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { +/* + * eb1a:2868 Reddo DVB-C USB TV Box + * GPIO4 - CU1216L NIM + * Other GPIOs seems to be don't care. + */ +static const struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10}, {EM2820_R08_GPIO_CTRL, 0xde, 0xff, 10}, {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10}, @@ -248,7 +243,7 @@ static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { }; /* Callback for the most boards */ -static struct em28xx_reg_seq default_tuner_gpio[] = { +static const struct em28xx_reg_seq default_tuner_gpio[] = { {EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10}, {EM2820_R08_GPIO_CTRL, 0, EM_GPIO_4, 10}, {EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10}, @@ -256,69 +251,70 @@ static struct em28xx_reg_seq default_tuner_gpio[] = { }; /* Mute/unmute */ -static struct em28xx_reg_seq compro_unmute_tv_gpio[] = { +static const struct em28xx_reg_seq compro_unmute_tv_gpio[] = { {EM2820_R08_GPIO_CTRL, 5, 7, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq compro_unmute_svid_gpio[] = { +static const struct em28xx_reg_seq compro_unmute_svid_gpio[] = { {EM2820_R08_GPIO_CTRL, 4, 7, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq compro_mute_gpio[] = { +static const struct em28xx_reg_seq compro_mute_gpio[] = { {EM2820_R08_GPIO_CTRL, 6, 7, 10}, { -1, -1, -1, -1}, }; /* Terratec AV350 */ -static struct em28xx_reg_seq terratec_av350_mute_gpio[] = { +static const struct em28xx_reg_seq terratec_av350_mute_gpio[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0x7f, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { +static const struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq silvercrest_reg_seq[] = { +static const struct em28xx_reg_seq silvercrest_reg_seq[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2820_R08_GPIO_CTRL, 0x01, 0xf7, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq vc211a_enable[] = { +static const struct em28xx_reg_seq vc211a_enable[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0x07, 10}, {EM2820_R08_GPIO_CTRL, 0xff, 0x0f, 10}, {EM2820_R08_GPIO_CTRL, 0xff, 0x0b, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq dikom_dk300_digital[] = { +static const struct em28xx_reg_seq dikom_dk300_digital[] = { {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x08, 0xff, 10}, { -1, -1, -1, -1}, }; /* Reset for the most [digital] boards */ -static struct em28xx_reg_seq leadership_digital[] = { +static const struct em28xx_reg_seq leadership_digital[] = { {EM2874_R80_GPIO_P0_CTRL, 0x70, 0xff, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq leadership_reset[] = { +static const struct em28xx_reg_seq leadership_reset[] = { {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10}, { -1, -1, -1, -1}, }; -/* 2013:024f PCTV nanoStick T2 290e +/* + * 2013:024f PCTV nanoStick T2 290e * GPIO_6 - demod reset * GPIO_7 - LED */ -static struct em28xx_reg_seq pctv_290e[] = { +static const struct em28xx_reg_seq pctv_290e[] = { {EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 80}, {EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 80}, /* GPIO_6 = 1 */ {EM2874_R80_GPIO_P0_CTRL, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */ @@ -326,7 +322,7 @@ static struct em28xx_reg_seq pctv_290e[] = { }; #if 0 -static struct em28xx_reg_seq terratec_h5_gpio[] = { +static const struct em28xx_reg_seq terratec_h5_gpio[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50}, @@ -334,7 +330,7 @@ static struct em28xx_reg_seq terratec_h5_gpio[] = { { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq terratec_h5_digital[] = { +static const struct em28xx_reg_seq terratec_h5_digital[] = { {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10}, @@ -342,7 +338,8 @@ static struct em28xx_reg_seq terratec_h5_digital[] = { }; #endif -/* 2013:024f PCTV DVB-S2 Stick 460e +/* + * 2013:024f PCTV DVB-S2 Stick 460e * GPIO_0 - POWER_ON * GPIO_1 - BOOST * GPIO_2 - VUV_LNB (red LED) @@ -352,7 +349,7 @@ static struct em28xx_reg_seq terratec_h5_digital[] = { * GPIO_6 - RESET_DEM * GPIO_7 - LED (green LED) */ -static struct em28xx_reg_seq pctv_460e[] = { +static const struct em28xx_reg_seq pctv_460e[] = { {EM2874_R80_GPIO_P0_CTRL, 0x01, 0xff, 50}, { 0x0d, 0xff, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0x41, 0xff, 50}, /* GPIO_6=1 */ @@ -361,7 +358,7 @@ static struct em28xx_reg_seq pctv_460e[] = { { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq c3tech_digital_duo_digital[] = { +static const struct em28xx_reg_seq c3tech_digital_duo_digital[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 10}, /* xc5000 reset */ {EM2874_R80_GPIO_P0_CTRL, 0xf9, 0xff, 35}, @@ -384,7 +381,7 @@ static struct em28xx_reg_seq c3tech_digital_duo_digital[] = { * GPIO 6 = #RESET_DEM * GPIO 7 = P07_LED (green LED) */ -static struct em28xx_reg_seq pctv_461e[] = { +static const struct em28xx_reg_seq pctv_461e[] = { {EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0}, {0x0d, 0xff, 0xff, 0}, {EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */ @@ -396,7 +393,7 @@ static struct em28xx_reg_seq pctv_461e[] = { }; #if 0 -static struct em28xx_reg_seq hauppauge_930c_gpio[] = { +static const struct em28xx_reg_seq hauppauge_930c_gpio[] = { {EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0x4f, 0xff, 10}, /* xc5000 reset */ {EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10}, @@ -404,7 +401,7 @@ static struct em28xx_reg_seq hauppauge_930c_gpio[] = { { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq hauppauge_930c_digital[] = { +static const struct em28xx_reg_seq hauppauge_930c_digital[] = { {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10}, @@ -412,38 +409,41 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = { }; #endif -/* 1b80:e425 MaxMedia UB425-TC +/* + * 1b80:e425 MaxMedia UB425-TC * 1b80:e1cc Delock 61959 * GPIO_6 - demod reset, 0=active * GPIO_7 - LED, 0=active */ -static struct em28xx_reg_seq maxmedia_ub425_tc[] = { +static const struct em28xx_reg_seq maxmedia_ub425_tc[] = { {EM2874_R80_GPIO_P0_CTRL, 0x83, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */ {EM2874_R80_GPIO_P0_CTRL, 0x43, 0xff, 000}, /* GPIO_7 = 0 */ { -1, -1, -1, -1}, }; -/* 2304:0242 PCTV QuatroStick (510e) +/* + * 2304:0242 PCTV QuatroStick (510e) * GPIO_2: decoder reset, 0=active * GPIO_4: decoder suspend, 0=active * GPIO_6: demod reset, 0=active * GPIO_7: LED, 1=active */ -static struct em28xx_reg_seq pctv_510e[] = { +static const struct em28xx_reg_seq pctv_510e[] = { {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ { -1, -1, -1, -1}, }; -/* 2013:0251 PCTV QuatroStick nano (520e) +/* + * 2013:0251 PCTV QuatroStick nano (520e) * GPIO_2: decoder reset, 0=active * GPIO_4: decoder suspend, 0=active * GPIO_6: demod reset, 0=active * GPIO_7: LED, 1=active */ -static struct em28xx_reg_seq pctv_520e[] = { +static const struct em28xx_reg_seq pctv_520e[] = { {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ @@ -451,7 +451,8 @@ static struct em28xx_reg_seq pctv_520e[] = { { -1, -1, -1, -1}, }; -/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam +/* + * 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam * reg 0x80/0x84: * GPIO_0: capturing LED, 0=on, 1=off * GPIO_2: AV mute button, 0=pressed, 1=unpressed @@ -460,13 +461,13 @@ static struct em28xx_reg_seq pctv_520e[] = { * reg 0x81/0x85: * GPIO_7: snapshot button, 0=pressed, 1=unpressed */ -static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = { +static const struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = { {EM2820_R08_GPIO_CTRL, 0xf7, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xb2, 10}, { -1, -1, -1, -1}, }; -static struct em28xx_reg_seq pctv_292e[] = { +static const struct em28xx_reg_seq pctv_292e[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, {0x0d, 0xff, 0xff, 950}, {EM2874_R80_GPIO_P0_CTRL, 0xbd, 0xff, 100}, @@ -478,7 +479,7 @@ static struct em28xx_reg_seq pctv_292e[] = { {-1, -1, -1, -1}, }; -static struct em28xx_reg_seq terratec_t2_stick_hd[] = { +static const struct em28xx_reg_seq terratec_t2_stick_hd[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, {0x0d, 0xff, 0xff, 600}, {EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 10}, @@ -492,7 +493,7 @@ static struct em28xx_reg_seq terratec_t2_stick_hd[] = { {-1, -1, -1, -1}, }; -static struct em28xx_reg_seq plex_px_bcud[] = { +static const struct em28xx_reg_seq plex_px_bcud[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, {0x0d, 0xff, 0xff, 0}, {EM2874_R50_IR_CONFIG, 0x01, 0xff, 0}, @@ -507,8 +508,10 @@ static struct em28xx_reg_seq plex_px_bcud[] = { }; /* - * 2040:0265 Hauppauge WinTV-dualHD DVB - * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM + * 2040:0265 Hauppauge WinTV-dualHD DVB Isoc + * 2040:8265 Hauppauge WinTV-dualHD DVB Bulk + * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM Isoc + * 2040:826d Hauppauge WinTV-dualHD ATSC/QAM Bulk * reg 0x80/0x84: * GPIO_0: Yellow LED tuner 1, 0=on, 1=off * GPIO_1: Green LED tuner 1, 0=on, 1=off @@ -517,7 +520,7 @@ static struct em28xx_reg_seq plex_px_bcud[] = { * GPIO_5: Reset #2, 0=active * GPIO_6: Reset #1, 0=active */ -static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { +static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, {0x0d, 0xff, 0xff, 200}, {0x50, 0x04, 0xff, 300}, @@ -534,7 +537,7 @@ static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { /* * Button definitions */ -static struct em28xx_button std_snapshot_button[] = { +static const struct em28xx_button std_snapshot_button[] = { { .role = EM28XX_BUTTON_SNAPSHOT, .reg_r = EM28XX_R0C_USBSUSP, @@ -545,7 +548,7 @@ static struct em28xx_button std_snapshot_button[] = { {-1, 0, 0, 0, 0}, }; -static struct em28xx_button speedlink_vad_laplace_buttons[] = { +static const struct em28xx_button speedlink_vad_laplace_buttons[] = { { .role = EM28XX_BUTTON_SNAPSHOT, .reg_r = EM2874_R85_GPIO_P1_STATE, @@ -629,7 +632,7 @@ static struct em28xx_led hauppauge_dualhd_leds[] = { /* * Board definitions */ -struct em28xx_board em28xx_boards[] = { +const struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "EM2710/EM2750/EM2751 webcam grabber", .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, @@ -1436,9 +1439,11 @@ struct em28xx_board em28xx_boards[] = { .gpio = default_analog, } }, }, - /* maybe there's a reason behind it why Terratec sells the Hybrid XS - as Prodigy XS with a different PID, let's keep it separated for now - maybe we'll need it lateron */ + /* + * maybe there's a reason behind it why Terratec sells the Hybrid XS + * as Prodigy XS with a different PID, let's keep it separated for now + * maybe we'll need it later on + */ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", .tuner_type = TUNER_XC2028, @@ -1782,8 +1787,9 @@ struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_KWORLD_315U, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, - /* Analog mode - still not ready */ - /*.input = { { +#if 0 + /* FIXME: Analog mode - still not ready */ + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -1801,7 +1807,8 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, .gpio = em2882_kworld_315u_analog1, .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - } }, */ + } }, +#endif }, [EM2880_BOARD_EMPIRE_DUAL_TV] = { .name = "Empire dual TV", @@ -2162,17 +2169,21 @@ struct em28xx_board em28xx_boards[] = { .gpio = evga_indtube_analog, } }, }, - /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + - Infineon TUA6034) */ + /* + * eb1a:2868 Empia EM2870 + Philips CU1216L NIM + * (Philips TDA10023 + Infineon TUA6034) + */ [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { .name = "Reddo DVB-C USB TV Box", .tuner_type = TUNER_ABSENT, .tuner_gpio = reddo_dvb_c_usb_box, .has_dvb = 1, }, - /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold + /* + * 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold * initially as the KWorld PlusTV 340U, then as the UB435-Q. - * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */ + * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 + */ [EM2870_BOARD_KWORLD_A340] = { .name = "KWorld PlusTV 340U or UB435-Q (ATSC)", .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ @@ -2180,30 +2191,38 @@ struct em28xx_board em28xx_boards[] = { .dvb_gpio = kworld_a340_digital, .tuner_gpio = default_tuner_gpio, }, - /* 2013:024f PCTV nanoStick T2 290e. - * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */ + /* + * 2013:024f PCTV nanoStick T2 290e. + * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 + */ [EM28174_BOARD_PCTV_290E] = { .name = "PCTV nanoStick T2 290e", .def_i2c_bus = 1, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ, .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_290e, .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, - /* 2013:024f PCTV DVB-S2 Stick 460e - * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */ + /* + * 2013:024f PCTV DVB-S2 Stick 460e + * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 + */ [EM28174_BOARD_PCTV_460E] = { .def_i2c_bus = 1, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, .name = "PCTV DVB-S2 Stick (460e)", .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_460e, .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, - /* eb1a:5006 Honestech VIDBOX NW03 - * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */ + /* + * eb1a:5006 Honestech VIDBOX NW03 + * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner + */ [EM2860_BOARD_HT_VIDBOX_NW03] = { .name = "Honestech Vidbox NW03", .tuner_type = TUNER_ABSENT, @@ -2214,12 +2233,14 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */ + .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs check */ .amux = EM28XX_AMUX_LINE_IN, } }, }, - /* 1b80:e425 MaxMedia UB425-TC - * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */ + /* + * 1b80:e425 MaxMedia UB425-TC + * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 + */ [EM2874_BOARD_MAXMEDIA_UB425_TC] = { .name = "MaxMedia UB425-TC", .tuner_type = TUNER_ABSENT, @@ -2230,8 +2251,10 @@ struct em28xx_board em28xx_boards[] = { .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, - /* 2304:0242 PCTV QuatroStick (510e) - * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + /* + * 2304:0242 PCTV QuatroStick (510e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 + */ [EM2884_BOARD_PCTV_510E] = { .name = "PCTV QuatroStick (510e)", .tuner_type = TUNER_ABSENT, @@ -2242,8 +2265,10 @@ struct em28xx_board em28xx_boards[] = { .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, - /* 2013:0251 PCTV QuatroStick nano (520e) - * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + /* + * 2013:0251 PCTV QuatroStick nano (520e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 + */ [EM2884_BOARD_PCTV_520E] = { .name = "PCTV QuatroStick nano (520e)", .tuner_type = TUNER_ABSENT, @@ -2263,9 +2288,11 @@ struct em28xx_board em28xx_boards[] = { .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, - /* 1b80:e1cc Delock 61959 + /* + * 1b80:e1cc Delock 61959 * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 - * mostly the same as MaxMedia UB-425-TC but different remote */ + * mostly the same as MaxMedia UB-425-TC but different remote + */ [EM2874_BOARD_DELOCK_61959] = { .name = "Delock 61959", .tuner_type = TUNER_ABSENT, @@ -2311,8 +2338,10 @@ struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_PINNACLE_PCTV_HD, .leds = pctv_80e_leds, }, - /* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam - * Empia EM2765 + OmniVision OV2640 */ + /* + * 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam + * Empia EM2765 + OmniVision OV2640 + */ [EM2765_BOARD_SPEEDLINK_VAD_LAPLACE] = { .name = "SpeedLink Vicious And Devine Laplace webcam", .xclk = EM28XX_XCLK_FREQUENCY_24MHZ, @@ -2329,23 +2358,29 @@ struct em28xx_board em28xx_boards[] = { .buttons = speedlink_vad_laplace_buttons, .leds = speedlink_vad_laplace_leds, }, - /* 2013:0258 PCTV DVB-S2 Stick (461e) - * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293 */ + /* + * 2013:0258 PCTV DVB-S2 Stick (461e) + * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293 + */ [EM28178_BOARD_PCTV_461E] = { .def_i2c_bus = 1, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, .name = "PCTV DVB-S2 Stick (461e)", .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_461e, .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, - /* 2013:025f PCTV tripleStick (292e). - * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */ + /* + * 2013:025f PCTV tripleStick (292e). + * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 + */ [EM28178_BOARD_PCTV_292E] = { .name = "PCTV tripleStick (292e)", .def_i2c_bus = 1, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, .tuner_type = TUNER_ABSENT, .tuner_gpio = pctv_292e, .has_dvb = 1, @@ -2365,12 +2400,15 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, - /* eb1a:8179 Terratec Cinergy T2 Stick HD. - * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2146 */ + /* + * eb1a:8179 Terratec Cinergy T2 Stick HD. + * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2146 + */ [EM28178_BOARD_TERRATEC_T2_STICK_HD] = { .name = "Terratec Cinergy T2 Stick HD", .def_i2c_bus = 1, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, .tuner_type = TUNER_ABSENT, .tuner_gpio = terratec_t2_stick_hd, .has_dvb = 1, @@ -2391,7 +2429,8 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, }, /* - * 2040:0265 Hauppauge WinTV-dualHD (DVB version). + * 2040:0265 Hauppauge WinTV-dualHD (DVB version) Isoc. + * 2040:8265 Hauppauge WinTV-dualHD (DVB version) Bulk. * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157 */ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = { @@ -2402,11 +2441,13 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .tuner_gpio = hauppauge_dualhd_dvb, .has_dvb = 1, + .has_dual_ts = 1, .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, /* - * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM). + * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Isoc. + * 2040:826d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Bulk. * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157 */ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = { @@ -2417,6 +2458,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .tuner_gpio = hauppauge_dualhd_dvb, .has_dvb = 1, + .has_dual_ts = 1, .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, @@ -2485,10 +2527,10 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0xeb1a, 0xe359), .driver_info = EM2870_BOARD_KWORLD_355U }, - { USB_DEVICE(0x1b80, 0xe302), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ - { USB_DEVICE(0x1b80, 0xe304), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ + { USB_DEVICE(0x1b80, 0xe302), /* Kaiser Baas Video to DVD maker */ + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x1b80, 0xe304), /* Kworld DVD Maker 2 */ + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -2547,8 +2589,12 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, { USB_DEVICE(0x2040, 0x0265), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, + { USB_DEVICE(0x2040, 0x8265), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, { USB_DEVICE(0x2040, 0x026d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x826d), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -2609,7 +2655,13 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PCTV_461E }, { USB_DEVICE(0x2013, 0x025f), .driver_info = EM28178_BOARD_PCTV_292E }, - { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD */ + { USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */ + .driver_info = EM28178_BOARD_PCTV_292E }, + { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD Isoc */ + .driver_info = EM28178_BOARD_PCTV_292E }, + { USB_DEVICE(0x2040, 0x8264), /* Hauppauge OEM Generic WinTV-soloHD Bulk */ + .driver_info = EM28178_BOARD_PCTV_292E }, + { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */ .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x0413, 0x6f07), .driver_info = EM2861_BOARD_LEADTEK_VC100 }, @@ -2626,7 +2678,7 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table); /* * EEPROM hash table for devices with generic USB IDs */ -static struct em28xx_hash_table em28xx_eeprom_hash[] = { +static const struct em28xx_hash_table em28xx_eeprom_hash[] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, @@ -2639,7 +2691,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { }; /* I2C devicelist hash table for devices with generic USB IDs */ -static struct em28xx_hash_table em28xx_i2c_hash[] = { +static const struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, @@ -2669,26 +2721,46 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) } EXPORT_SYMBOL_GPL(em28xx_tuner_callback); -static inline void em28xx_set_model(struct em28xx *dev) +static inline void em28xx_set_xclk_i2c_speed(struct em28xx *dev) { - dev->board = em28xx_boards[dev->model]; + const struct em28xx_board *board = &em28xx_boards[dev->model]; + u8 xclk = board->xclk, i2c_speed = board->i2c_speed; - /* Those are the default values for the majority of boards - Use those values if not specified otherwise at boards entry + /* + * Those are the default values for the majority of boards + * Use those values if not specified otherwise at boards entry */ - if (!dev->board.xclk) - dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ; + if (!xclk) + xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ; + + em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); - if (!dev->board.i2c_speed) - dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ; + if (!i2c_speed) + i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; + + dev->i2c_speed = i2c_speed & 0x03; + + if (!dev->board.is_em2800) + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, i2c_speed); + msleep(50); +} + +static inline void em28xx_set_model(struct em28xx *dev) +{ + dev->board = em28xx_boards[dev->model]; + dev->has_msp34xx = dev->board.has_msp34xx; + dev->is_webcam = dev->board.is_webcam; + + em28xx_set_xclk_i2c_speed(dev); /* Should be initialized early, for I2C to work */ dev->def_i2c_bus = dev->board.def_i2c_bus; } -/* Wait until AC97_RESET reports the expected value reliably before proceeding. +/* + * Wait until AC97_RESET reports the expected value reliably before proceeding. * We also check that two unrelated registers accesses don't return the same * value to avoid premature return. * This procedure helps ensuring AC97 register accesses are reliable. @@ -2718,17 +2790,17 @@ static int em28xx_wait_until_ac97_features_equals(struct em28xx *dev, return -ETIMEDOUT; } -/* Since em28xx_pre_card_setup() requires a proper dev->model, +/* + * Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ static void em28xx_pre_card_setup(struct em28xx *dev) { - /* Set the initial XCLK and I2C clock values based on the board - definition */ - em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); - if (!dev->board.is_em2800) - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); - msleep(50); + /* + * Set the initial XCLK and I2C clock values based on the board + * definition + */ + em28xx_set_xclk_i2c_speed(dev); /* request some modules */ switch (dev->model) { @@ -2739,17 +2811,19 @@ static void em28xx_pre_card_setup(struct em28xx *dev) case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x6d); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x7d); - msleep(10); + usleep_range(10000, 11000); break; case EM2870_BOARD_COMPRO_VIDEOMATE: - /* TODO: someone can do some cleanup here... - not everything's needed */ + /* + * TODO: someone can do some cleanup here... + * not everything's needed + */ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); mdelay(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc); @@ -2760,8 +2834,10 @@ static void em28xx_pre_card_setup(struct em28xx *dev) mdelay(70); break; case EM2870_BOARD_TERRATEC_XS_MT2060: - /* this device needs some gpio writes to get the DVB-T - demod work */ + /* + * this device needs some gpio writes to get the DVB-T + * demod work + */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); mdelay(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); @@ -2770,8 +2846,10 @@ static void em28xx_pre_card_setup(struct em28xx *dev) mdelay(70); break; case EM2870_BOARD_PINNACLE_PCTV_DVB: - /* this device needs some gpio writes to get the - DVB-T demod work */ + /* + * this device needs some gpio writes to get the + * DVB-T demod work + */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); mdelay(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); @@ -2787,13 +2865,13 @@ static void em28xx_pre_card_setup(struct em28xx *dev) case EM2882_BOARD_KWORLD_ATSC_315U: em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2880_R04_GPO, 0x08); - msleep(10); + usleep_range(10000, 11000); break; case EM2860_BOARD_KAIOMY_TVNPC_U2: @@ -2801,11 +2879,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); em28xx_write_regs(dev, 0x0d, "\x42", 1); em28xx_write_regs(dev, 0x08, "\xfd", 1); - msleep(10); + usleep_range(10000, 11000); em28xx_write_regs(dev, 0x08, "\xff", 1); - msleep(10); + usleep_range(10000, 11000); em28xx_write_regs(dev, 0x08, "\x7f", 1); - msleep(10); + usleep_range(10000, 11000); em28xx_write_regs(dev, 0x08, "\x6b", 1); break; @@ -2817,7 +2895,7 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); @@ -2825,7 +2903,8 @@ static void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2860_BOARD_TERRATEC_GRABBY: - /* HACK?: Ensure AC97 register reading is reliable before + /* + * HACK?: Ensure AC97 register reading is reliable before * proceeding. In practice, this will wait about 1.6 seconds. */ em28xx_wait_until_ac97_features_equals(dev, 0x6a90); @@ -2843,7 +2922,7 @@ static int em28xx_hint_board(struct em28xx *dev) { int i; - if (dev->board.is_webcam) { + if (dev->is_webcam) { if (dev->em28xx_sensor == EM28XX_MT9V011) { dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; } else if (dev->em28xx_sensor == EM28XX_MT9M001 || @@ -2855,7 +2934,8 @@ static int em28xx_hint_board(struct em28xx *dev) return 0; } - /* HINT method: EEPROM + /* + * HINT method: EEPROM * * This method works only for boards with eeprom. * Uses a hash of all eeprom bytes. The hash should be @@ -2881,7 +2961,8 @@ static int em28xx_hint_board(struct em28xx *dev) } } - /* HINT method: I2C attached devices + /* + * HINT method: I2C attached devices * * This method works for all boards. * Uses a hash of i2c scanned devices. @@ -2935,11 +3016,11 @@ static void em28xx_card_setup(struct em28xx *dev) * If the device can be a webcam, seek for a sensor. * If sensor is not found, then it isn't a webcam. */ - if (dev->board.is_webcam) { + if (dev->is_webcam) { em28xx_detect_sensor(dev); if (dev->em28xx_sensor == EM28XX_NOSENSOR) /* NOTE: error/unknown sensor/no sensor */ - dev->board.is_webcam = 0; + dev->is_webcam = 0; } switch (dev->model) { @@ -2959,9 +3040,9 @@ static void em28xx_card_setup(struct em28xx *dev) * This solution is only valid if they do not share eeprom * hash identities which has not been determined as yet. */ - if (em28xx_hint_board(dev) < 0) + if (em28xx_hint_board(dev) < 0) { dev_err(&dev->intf->dev, "Board not discovered\n"); - else { + } else { em28xx_set_model(dev); em28xx_pre_card_setup(dev); } @@ -2971,7 +3052,7 @@ static void em28xx_card_setup(struct em28xx *dev) } dev_info(&dev->intf->dev, "Identified as %s (card=%d)\n", - dev->board.name, dev->model); + dev->board.name, dev->model); dev->tuner_type = em28xx_boards[dev->model].tuner_type; @@ -2988,7 +3069,7 @@ static void em28xx_card_setup(struct em28xx *dev) { struct tveeprom tv; - if (dev->eedata == NULL) + if (!dev->eedata) break; #if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); @@ -3001,15 +3082,15 @@ static void em28xx_card_setup(struct em28xx *dev) if (tv.audio_processor == TVEEPROM_AUDPROC_MSP) { dev->i2s_speed = 2048000; - dev->board.has_msp34xx = 1; + dev->has_msp34xx = 1; } break; } case EM2882_BOARD_KWORLD_ATSC_315U: em28xx_write_reg(dev, 0x0d, 0x42); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); - msleep(10); + usleep_range(10000, 11000); break; case EM2820_BOARD_KWORLD_PVRTV2800RF: /* GPIO enables sound on KWORLD PVR TV 2800RF */ @@ -3034,10 +3115,12 @@ static void em28xx_card_setup(struct em28xx *dev) if (!em28xx_hint_board(dev)) em28xx_set_model(dev); - /* In cases where we had to use a board hint, the call to - em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, - so make the call now so the analog GPIOs are set properly - before probing the i2c bus. */ + /* + * In cases where we had to use a board hint, the call to + * em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + * so make the call now so the analog GPIOs are set properly + * before probing the i2c bus. + */ em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; @@ -3059,10 +3142,12 @@ static void em28xx_card_setup(struct em28xx *dev) if (!em28xx_hint_board(dev)) em28xx_set_model(dev); - /* In cases where we had to use a board hint, the call to - em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, - so make the call now so the analog GPIOs are set properly - before probing the i2c bus. */ + /* + * In cases where we had to use a board hint, the call to + * em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + * so make the call now so the analog GPIOs are set properly + * before probing the i2c bus. + */ em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; @@ -3147,8 +3232,8 @@ static void request_module_async(struct work_struct *work) */ /* - * Devicdes with an audio-only interface also have a V4L/DVB/RC - * interface. Don't register extensions twice on those devices. + * Devices with an audio-only intf also have a V4L/DVB/RC + * intf. Don't register extensions twice on those devices. */ if (dev->is_audio_only) { #if defined(CONFIG_MODULES) && defined(MODULE) @@ -3209,7 +3294,6 @@ static int em28xx_media_device_init(struct em28xx *dev, static void em28xx_unregister_media_device(struct em28xx *dev) { - #ifdef CONFIG_MEDIA_CONTROLLER if (dev->media_dev) { media_device_unregister(dev->media_dev); @@ -3224,7 +3308,7 @@ static void em28xx_unregister_media_device(struct em28xx *dev) * em28xx_release_resources() * unregisters the v4l2,i2c and usb devices * called when the device gets disconnected or at module unload -*/ + */ static void em28xx_release_resources(struct em28xx *dev) { struct usb_device *udev = interface_to_usbdev(dev->intf); @@ -3239,7 +3323,8 @@ static void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); - usb_put_dev(udev); + if (dev->ts == PRIMARY_TS) + usb_put_dev(udev); /* Mark device as unused */ clear_bit(dev->devno, em28xx_devused); @@ -3273,13 +3358,13 @@ EXPORT_SYMBOL_GPL(em28xx_free_device); * allocates and inits the device structs, registers i2c bus and v4l device */ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, - struct usb_interface *interface, + struct usb_interface *intf, int minor) { int retval; const char *chip_name = NULL; - dev->intf = interface; + dev->intf = intf; mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); @@ -3382,17 +3467,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); - if (!dev->board.is_em2800) { - /* Resets I2C speed */ - retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); - if (retval < 0) { - dev_err(&dev->intf->dev, - "%s: em28xx_write_reg failed! retval [%d]\n", - __func__, retval); - return retval; - } - } - rt_mutex_init(&dev->i2c_bus_lock); /* register i2c bus 0 */ @@ -3417,8 +3491,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, EM28XX_I2C_ALGO_EM28XX); if (retval < 0) { dev_err(&dev->intf->dev, - "%s: em28xx_i2c_register bus 1 - error [%d]!\n", - __func__, retval); + "%s: em28xx_i2c_register bus 1 - error [%d]!\n", + __func__, retval); em28xx_i2c_unregister(dev, 0); @@ -3432,14 +3506,147 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return 0; } +static int em28xx_duplicate_dev(struct em28xx *dev) +{ + int nr; + struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL); + + if (!sec_dev) { + dev->dev_next = NULL; + return -ENOMEM; + } + memcpy(sec_dev, dev, sizeof(*sec_dev)); + /* Check to see next free device and mark as used */ + do { + nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS); + if (nr >= EM28XX_MAXBOARDS) { + /* No free device slots */ + dev_warn(&dev->intf->dev, ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + kfree(sec_dev); + dev->dev_next = NULL; + return -ENOMEM; + } + } while (test_and_set_bit(nr, em28xx_devused)); + sec_dev->devno = nr; + snprintf(sec_dev->name, 28, "em28xx #%d", nr); + sec_dev->dev_next = NULL; + dev->dev_next = sec_dev; + return 0; +} + /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +static void em28xx_check_usb_descriptor(struct em28xx *dev, + struct usb_device *udev, + struct usb_interface *intf, + int alt, int ep, + bool *has_vendor_audio, + bool *has_video, + bool *has_dvb) +{ + const struct usb_endpoint_descriptor *e; + int sizedescr, size; + + /* + * NOTE: + * + * Old logic with support for isoc transfers only was: + * 0x82 isoc => analog + * 0x83 isoc => audio + * 0x84 isoc => digital + * + * New logic with support for bulk transfers + * 0x82 isoc => analog + * 0x82 bulk => analog + * 0x83 isoc* => audio + * 0x84 isoc => digital + * 0x84 bulk => analog or digital** + * 0x85 isoc => digital TS2 + * 0x85 bulk => digital TS2 + * (*: audio should always be isoc) + * (**: analog, if ep 0x82 is isoc, otherwise digital) + * + * The new logic preserves backwards compatibility and + * reflects the endpoint configurations we have seen + * so far. But there might be devices for which this + * logic is not sufficient... + */ + + e = &intf->altsetting[alt].endpoint[ep].desc; + + if (!usb_endpoint_dir_in(e)) + return; + + sizedescr = le16_to_cpu(e->wMaxPacketSize); + size = sizedescr & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(sizedescr); + + /* Only inspect input endpoints */ + + switch (e->bEndpointAddress) { + case 0x82: + *has_video = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[alt] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = e->bEndpointAddress; + } + return; + case 0x83: + if (usb_endpoint_xfer_isoc(e)) + *has_vendor_audio = true; + else + dev_err(&intf->dev, + "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); + return; + case 0x84: + if (*has_video && (usb_endpoint_xfer_bulk(e))) { + dev->analog_ep_bulk = e->bEndpointAddress; + } else { + if (usb_endpoint_xfer_isoc(e)) { + if (size > dev->dvb_max_pkt_size_isoc) { + /* + * 2) some manufacturers (e.g. Terratec) + * disable endpoints by setting + * wMaxPacketSize to 0 bytes for all + * alt settings. So far, we've seen + * this for DVB isoc endpoints only. + */ + *has_dvb = true; + dev->dvb_ep_isoc = e->bEndpointAddress; + dev->dvb_max_pkt_size_isoc = size; + dev->dvb_alt_isoc = alt; + } + } else { + *has_dvb = true; + dev->dvb_ep_bulk = e->bEndpointAddress; + } + } + return; + case 0x85: + if (usb_endpoint_xfer_isoc(e)) { + if (size > dev->dvb_max_pkt_size_isoc_ts2) { + dev->dvb_ep_isoc_ts2 = e->bEndpointAddress; + dev->dvb_max_pkt_size_isoc_ts2 = size; + dev->dvb_alt_isoc = alt; + } + } else { + dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; + } + return; + } +} + /* * em28xx_usb_probe() * checks for supported devices */ -static int em28xx_usb_probe(struct usb_interface *interface, +static int em28xx_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; @@ -3447,17 +3654,17 @@ static int em28xx_usb_probe(struct usb_interface *interface, int retval; bool has_vendor_audio = false, has_video = false, has_dvb = false; int i, nr, try_bulk; - const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; + const int ifnum = intf->altsetting[0].desc.bInterfaceNumber; char *speed; - udev = usb_get_dev(interface_to_usbdev(interface)); + udev = usb_get_dev(interface_to_usbdev(intf)); /* Check to see next free device and mark as used */ do { nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS); if (nr >= EM28XX_MAXBOARDS) { /* No free device slots */ - dev_err(&interface->dev, + dev_err(&intf->dev, "Driver supports up to %i em28xx boards.\n", EM28XX_MAXBOARDS); retval = -ENOMEM; @@ -3466,13 +3673,13 @@ static int em28xx_usb_probe(struct usb_interface *interface, } while (test_and_set_bit(nr, em28xx_devused)); /* Don't register audio interfaces */ - if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev_err(&interface->dev, + if (intf->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev_err(&intf->dev, "audio device (%04x:%04x): interface %i, class %i\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), ifnum, - interface->altsetting[0].desc.bInterfaceClass); + intf->altsetting[0].desc.bInterfaceClass); retval = -ENODEV; goto err; @@ -3480,106 +3687,33 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { + if (!dev) { retval = -ENOMEM; goto err; } /* compute alternate max packet sizes */ - dev->alt_max_pkt_size_isoc = - kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) * - interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size_isoc == NULL) { + dev->alt_max_pkt_size_isoc = kcalloc(intf->num_altsetting, + sizeof(dev->alt_max_pkt_size_isoc[0]), + GFP_KERNEL); + if (!dev->alt_max_pkt_size_isoc) { kfree(dev); retval = -ENOMEM; goto err; } /* Get endpoints */ - for (i = 0; i < interface->num_altsetting; i++) { + for (i = 0; i < intf->num_altsetting; i++) { int ep; - for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { - const struct usb_endpoint_descriptor *e; - int sizedescr, size; - - e = &interface->altsetting[i].endpoint[ep].desc; - - sizedescr = le16_to_cpu(e->wMaxPacketSize); - size = sizedescr & 0x7ff; - - if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult(sizedescr); - - if (usb_endpoint_dir_in(e)) { - switch (e->bEndpointAddress) { - case 0x82: - has_video = true; - if (usb_endpoint_xfer_isoc(e)) { - dev->analog_ep_isoc = - e->bEndpointAddress; - dev->alt_max_pkt_size_isoc[i] = size; - } else if (usb_endpoint_xfer_bulk(e)) { - dev->analog_ep_bulk = - e->bEndpointAddress; - } - break; - case 0x83: - if (usb_endpoint_xfer_isoc(e)) { - has_vendor_audio = true; - } else { - dev_err(&interface->dev, - "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); - } - break; - case 0x84: - if (has_video && - (usb_endpoint_xfer_bulk(e))) { - dev->analog_ep_bulk = - e->bEndpointAddress; - } else { - if (usb_endpoint_xfer_isoc(e)) { - if (size > dev->dvb_max_pkt_size_isoc) { - has_dvb = true; /* see NOTE (~) */ - dev->dvb_ep_isoc = e->bEndpointAddress; - dev->dvb_max_pkt_size_isoc = size; - dev->dvb_alt_isoc = i; - } - } else { - has_dvb = true; - dev->dvb_ep_bulk = e->bEndpointAddress; - } - } - break; - } - } - /* NOTE: - * Old logic with support for isoc transfers only was: - * 0x82 isoc => analog - * 0x83 isoc => audio - * 0x84 isoc => digital - * - * New logic with support for bulk transfers - * 0x82 isoc => analog - * 0x82 bulk => analog - * 0x83 isoc* => audio - * 0x84 isoc => digital - * 0x84 bulk => analog or digital** - * (*: audio should always be isoc) - * (**: analog, if ep 0x82 is isoc, otherwise digital) - * - * The new logic preserves backwards compatibility and - * reflects the endpoint configurations we have seen - * so far. But there might be devices for which this - * logic is not sufficient... - */ - /* - * NOTE (~): some manufacturers (e.g. Terratec) disable - * endpoints by setting wMaxPacketSize to 0 bytes for - * all alt settings. So far, we've seen this for - * DVB isoc endpoints only. - */ - } + for (ep = 0; + ep < intf->altsetting[i].desc.bNumEndpoints; + ep++) + em28xx_check_usb_descriptor(dev, udev, intf, + i, ep, + &has_vendor_audio, + &has_video, + &has_dvb); } if (!(has_vendor_audio || has_video || has_dvb)) { @@ -3602,7 +3736,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, speed = "unknown"; } - dev_err(&interface->dev, + dev_err(&intf->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n", udev->manufacturer ? udev->manufacturer : "", udev->product ? udev->product : "", @@ -3610,7 +3744,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), ifnum, - interface->altsetting->desc.bInterfaceNumber); + intf->altsetting->desc.bInterfaceNumber); /* * Make sure we have 480 Mbps of bandwidth, otherwise things like @@ -3618,8 +3752,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, * not enough even for most Digital TV streams. */ if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { - dev_err(&interface->dev, "Device initialization failed.\n"); - dev_err(&interface->dev, + dev_err(&intf->dev, "Device initialization failed.\n"); + dev_err(&intf->dev, "Device must be connected to a high-speed USB 2.0 port.\n"); retval = -ENODEV; goto err_free; @@ -3632,53 +3766,56 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->has_video = has_video; dev->ifnum = ifnum; + dev->ts = PRIMARY_TS; + snprintf(dev->name, 28, "em28xx"); + dev->dev_next = NULL; + if (has_vendor_audio) { - dev_err(&interface->dev, + dev_err(&intf->dev, "Audio interface %i found (Vendor Class)\n", ifnum); dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR; } - /* Checks if audio is provided by a USB Audio Class interface */ + /* Checks if audio is provided by a USB Audio Class intf */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { struct usb_interface *uif = udev->config->interface[i]; if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { if (has_vendor_audio) - dev_err(&interface->dev, + dev_err(&intf->dev, "em28xx: device seems to have vendor AND usb audio class interfaces !\n" - "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n"); + "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n"); dev->usb_audio_type = EM28XX_USB_AUDIO_CLASS; break; } } if (has_video) - dev_err(&interface->dev, "Video interface %i found:%s%s\n", + dev_err(&intf->dev, "Video interface %i found:%s%s\n", ifnum, dev->analog_ep_bulk ? " bulk" : "", dev->analog_ep_isoc ? " isoc" : ""); if (has_dvb) - dev_err(&interface->dev, "DVB interface %i found:%s%s\n", + dev_err(&intf->dev, "DVB interface %i found:%s%s\n", ifnum, dev->dvb_ep_bulk ? " bulk" : "", dev->dvb_ep_isoc ? " isoc" : ""); - dev->num_alt = interface->num_altsetting; + dev->num_alt = intf->num_altsetting; - if ((unsigned)card[nr] < em28xx_bcount) + if ((unsigned int)card[nr] < em28xx_bcount) dev->model = card[nr]; - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); + /* save our data pointer in this intf device */ + usb_set_intfdata(intf, dev); /* allocate device struct and check if the device is a webcam */ mutex_init(&dev->lock); - retval = em28xx_init_dev(dev, udev, interface, nr); - if (retval) { + retval = em28xx_init_dev(dev, udev, intf, nr); + if (retval) goto err_free; - } if (usb_xfer_mode < 0) { - if (dev->board.is_webcam) + if (dev->is_webcam) try_bulk = 1; else try_bulk = 0; @@ -3690,8 +3827,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_video && dev->board.decoder == EM28XX_NODECODER && dev->em28xx_sensor == EM28XX_NOSENSOR) { - - dev_err(&interface->dev, + dev_err(&intf->dev, "Currently, V4L2 is not supported on this model\n"); has_video = false; dev->has_video = false; @@ -3701,16 +3837,75 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_video) { if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) dev->analog_xfer_bulk = 1; - dev_err(&interface->dev, "analog set to %s mode.\n", + dev_err(&intf->dev, "analog set to %s mode.\n", dev->analog_xfer_bulk ? "bulk" : "isoc"); } if (has_dvb) { if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) dev->dvb_xfer_bulk = 1; - dev_err(&interface->dev, "dvb set to %s mode.\n", + dev_err(&intf->dev, "dvb set to %s mode.\n", dev->dvb_xfer_bulk ? "bulk" : "isoc"); } + if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) { + dev->dev_next->ts = SECONDARY_TS; + dev->dev_next->alt = -1; + dev->dev_next->is_audio_only = has_vendor_audio && + !(has_video || has_dvb); + dev->dev_next->has_video = false; + dev->dev_next->ifnum = ifnum; + dev->dev_next->model = id->driver_info; + + mutex_init(&dev->dev_next->lock); + retval = em28xx_init_dev(dev->dev_next, udev, intf, + dev->dev_next->devno); + if (retval) + goto err_free; + + dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */ + dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */ + + if (usb_xfer_mode < 0) { + if (dev->dev_next->is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = usb_xfer_mode > 0; + } + + /* Select USB transfer types to use */ + if (has_dvb) { + if (!dev->dvb_ep_isoc_ts2 || + (try_bulk && dev->dvb_ep_bulk_ts2)) + dev->dev_next->dvb_xfer_bulk = 1; + dev_info(&dev->intf->dev, "dvb ts2 set to %s mode.\n", + dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc"); + } + + dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2; + dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2; + dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2; + dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc; + + /* Configuare hardware to support TS2*/ + if (dev->dvb_xfer_bulk) { + /* The ep4 and ep5 are configuared for BULK */ + em28xx_write_reg(dev, 0x0b, 0x96); + mdelay(100); + em28xx_write_reg(dev, 0x0b, 0x80); + mdelay(100); + } else { + /* The ep4 and ep5 are configuared for ISO */ + em28xx_write_reg(dev, 0x0b, 0x96); + mdelay(100); + em28xx_write_reg(dev, 0x0b, 0x82); + mdelay(100); + } + + kref_init(&dev->dev_next->ref); + } + kref_init(&dev->ref); request_modules(dev); @@ -3743,45 +3938,59 @@ err_no_slot: * called when the device gets disconnected * video device will be unregistered on v4l2_close in case it is still open */ -static void em28xx_usb_disconnect(struct usb_interface *interface) +static void em28xx_usb_disconnect(struct usb_interface *intf) { struct em28xx *dev; - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + dev = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); if (!dev) return; + if (dev->dev_next) { + dev->dev_next->disconnected = 1; + dev_info(&dev->intf->dev, "Disconnecting %s\n", + dev->dev_next->name); + flush_request_modules(dev->dev_next); + } + dev->disconnected = 1; - dev_err(&dev->intf->dev, "Disconnecting\n"); + dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name); flush_request_modules(dev); em28xx_close_extension(dev); + if (dev->dev_next) + em28xx_release_resources(dev->dev_next); em28xx_release_resources(dev); + + if (dev->dev_next) { + kref_put(&dev->dev_next->ref, em28xx_free_device); + dev->dev_next = NULL; + } kref_put(&dev->ref, em28xx_free_device); } -static int em28xx_usb_suspend(struct usb_interface *interface, +static int em28xx_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct em28xx *dev; - dev = usb_get_intfdata(interface); + dev = usb_get_intfdata(intf); if (!dev) return 0; em28xx_suspend_extension(dev); return 0; } -static int em28xx_usb_resume(struct usb_interface *interface) +static int em28xx_usb_resume(struct usb_interface *intf) { struct em28xx *dev; - dev = usb_get_intfdata(interface); + dev = usb_get_intfdata(intf); if (!dev) return 0; em28xx_resume_extension(dev); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 1d0d8cc06103..36d341fb65dd 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1,26 +1,22 @@ -/* - em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> - Markus Rechberger <mrechberger@gmail.com> - Mauro Carvalho Chehab <mchehab@infradead.org> - Sascha Sommer <saschasommer@freenet.de> - Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices +// +// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> +// Markus Rechberger <mrechberger@gmail.com> +// Mauro Carvalho Chehab <mchehab@infradead.org> +// Sascha Sommer <saschasommer@freenet.de> +// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// 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 "em28xx.h" @@ -41,7 +37,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_VERSION(EM28XX_VERSION); /* #define ENABLE_DEBUG_ISOC_FRAMES */ @@ -60,7 +56,6 @@ static unsigned int reg_debug; module_param(reg_debug, int, 0644); MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); - #define em28xx_regdbg(fmt, arg...) do { \ if (reg_debug) \ dev_printk(KERN_DEBUG, &dev->intf->dev, \ @@ -97,10 +92,11 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, 0x0000, reg, dev->urb_buf, len, HZ); if (ret < 0) { em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed with error %i\n", - pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8, ret); + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8, ret); mutex_unlock(&dev->ctrl_urb_lock); return usb_translate_errors(ret); } @@ -111,10 +107,10 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, mutex_unlock(&dev->ctrl_urb_lock); em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x <<< %*ph\n", - pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8, len, buf); + pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8, len, buf); return ret; } @@ -155,7 +151,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if (dev->disconnected) return -ENODEV; - if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) + if (len < 1 || len > URB_MAX_CTRL_SIZE) return -EINVAL; mutex_lock(&dev->ctrl_urb_lock); @@ -341,8 +337,9 @@ static int set_ac97_input(struct em28xx *dev) int ret, i; enum em28xx_amux amux = dev->ctl_ainput; - /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that - em28xx should point to LINE IN, while AC97 should use VIDEO + /* + * EM28XX_AMUX_VIDEO2 is a special case used to indicate that + * em28xx should point to LINE IN, while AC97 should use VIDEO */ if (amux == EM28XX_AMUX_VIDEO2) amux = EM28XX_AMUX_VIDEO; @@ -378,9 +375,9 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } - if (dev->board.has_msp34xx) + if (dev->has_msp34xx) { input = EM28XX_AUDIO_SRC_TUNER; - else { + } else { switch (dev->ctl_ainput) { case EM28XX_AMUX_VIDEO: input = EM28XX_AUDIO_SRC_TUNER; @@ -399,7 +396,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); if (ret < 0) return ret; - msleep(5); + usleep_range(10000, 11000); switch (dev->audio_mode.ac97) { case EM28XX_NO_AC97: @@ -432,8 +429,9 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) return 0; - /* It is assumed that all devices use master volume for output. - It would be possible to use also line output. + /* + * It is assumed that all devices use master volume for output. + * It would be possible to use also line output. */ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { /* Mute all outputs */ @@ -453,7 +451,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); if (ret < 0) return ret; - msleep(10); + usleep_range(10000, 11000); /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); @@ -487,8 +485,10 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) { int sel = ac97_return_record_select(dev->ctl_aoutput); - /* Use the same input for both left and right - channels */ + /* + * Use the same input for both left and right + * channels + */ sel |= (sel << 8); em28xx_write_ac97(dev, AC97_REC_SEL, sel); @@ -539,7 +539,7 @@ int em28xx_audio_setup(struct em28xx *dev) else i2s_samplerates = 3; dev_info(&dev->intf->dev, "I2S Audio (%d sample rate(s))\n", - i2s_samplerates); + i2s_samplerates); /* Skip the code that does AC97 vendor detection */ dev->audio_mode.ac97 = EM28XX_NO_AC97; goto init_audio; @@ -579,7 +579,7 @@ int em28xx_audio_setup(struct em28xx *dev) dev_warn(&dev->intf->dev, "AC97 features = 0x%04x\n", feat); /* Try to identify what audio processor we have */ - if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90)) + if ((vid == 0xffffffff || vid == 0x83847650) && feat == 0x6a90) dev->audio_mode.ac97 = EM28XX_AC97_EM202; else if ((vid >> 8) == 0x838476) dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL; @@ -638,10 +638,29 @@ int em28xx_capture_start(struct em28xx *dev, int start) dev->chip_id == CHIP_ID_EM28174 || dev->chip_id == CHIP_ID_EM28178) { /* The Transport Stream Enable Register moved in em2874 */ - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - start ? - EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE); + if (dev->dvb_xfer_bulk) { + /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + 0xff); + } else { + /* ISOC Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + dev->dvb_max_pkt_size_isoc / 188); + } + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE); + else + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE); } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -651,7 +670,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; if (start) { - if (dev->board.is_webcam) + if (dev->is_webcam) rc = em28xx_write_reg(dev, 0x13, 0x0c); /* Enable video capture */ @@ -670,7 +689,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (rc < 0) return rc; - msleep(6); + usleep_range(10000, 11000); } else { /* disable video capture */ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); @@ -691,7 +710,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) +int em28xx_gpio_set(struct em28xx *dev, const struct em28xx_reg_seq *gpio) { int rc = 0; @@ -704,7 +723,7 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); else em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); - msleep(6); + usleep_range(10000, 11000); } /* Send GPIO reset sequences specified at board entry */ @@ -748,9 +767,9 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) } EXPORT_SYMBOL_GPL(em28xx_set_mode); -/* ------------------------------------------------------------------ - URB control - ------------------------------------------------------------------*/ +/* + *URB control + */ /* * URB completion handler for isoc/bulk transfers @@ -769,7 +788,7 @@ static void em28xx_irq_callback(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - em28xx_isocdbg("urb completition error %d.\n", urb->status); + em28xx_isocdbg("urb completion error %d.\n", urb->status); break; } @@ -800,11 +819,9 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; struct em28xx_usb_bufs *usb_bufs; - struct usb_device *udev = interface_to_usbdev(dev->intf); int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n", - mode); + em28xx_isocdbg("called %s in mode %d\n", __func__, mode); if (mode == EM28XX_DIGITAL_MODE) usb_bufs = &dev->usb_ctl.digital_bufs; @@ -819,23 +836,16 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) else usb_unlink_urb(urb); - if (usb_bufs->transfer_buffer[i]) { - usb_free_coherent(udev, - urb->transfer_buffer_length, - usb_bufs->transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); usb_bufs->urb[i] = NULL; } - usb_bufs->transfer_buffer[i] = NULL; } kfree(usb_bufs->urb); - kfree(usb_bufs->transfer_buffer); + kfree(usb_bufs->buf); usb_bufs->urb = NULL; - usb_bufs->transfer_buffer = NULL; + usb_bufs->buf = NULL; usb_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); @@ -851,7 +861,7 @@ void em28xx_stop_urbs(struct em28xx *dev) struct urb *urb; struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs; - em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + em28xx_isocdbg("called %s\n", __func__); for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; @@ -880,10 +890,12 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, int sb_size, pipe; int j, k; - em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode); - /* Check mode and if we have an endpoint for the selected - transfer type, select buffer */ + /* + * Check mode and if we have an endpoint for the selected + * transfer type, select buffer + */ if (mode == EM28XX_DIGITAL_MODE) { if ((xfer_bulk && !dev->dvb_ep_bulk) || (!xfer_bulk && !dev->dvb_ep_isoc)) { @@ -912,14 +924,13 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, usb_bufs->num_bufs = num_bufs; - usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + usb_bufs->urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!usb_bufs->urb) return -ENOMEM; - usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!usb_bufs->transfer_buffer) { - kfree(usb_bufs->urb); + usb_bufs->buf = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); + if (!usb_bufs->buf) { + kfree(usb_bufs->buf); return -ENOMEM; } @@ -942,37 +953,36 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, } usb_bufs->urb[i] = urb; - usb_bufs->transfer_buffer[i] = usb_alloc_coherent(udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!usb_bufs->transfer_buffer[i]) { - dev_err(&dev->intf->dev, - "unable to allocate %i bytes for transfer buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); + usb_bufs->buf[i] = kzalloc(sb_size, GFP_KERNEL); + if (!usb_bufs->buf[i]) { em28xx_uninit_usb_xfer(dev, mode); + + for (i--; i >= 0; i--) + kfree(usb_bufs->buf[i]); + + kfree(usb_bufs->buf); + usb_bufs->buf = NULL; + return -ENOMEM; } - memset(usb_bufs->transfer_buffer[i], 0, sb_size); + + urb->transfer_flags = URB_FREE_BUFFER; if (xfer_bulk) { /* bulk */ pipe = usb_rcvbulkpipe(udev, mode == EM28XX_ANALOG_MODE ? dev->analog_ep_bulk : dev->dvb_ep_bulk); - usb_fill_bulk_urb(urb, udev, pipe, - usb_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev); - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + usb_fill_bulk_urb(urb, udev, pipe, usb_bufs->buf[i], + sb_size, em28xx_irq_callback, dev); } else { /* isoc */ pipe = usb_rcvisocpipe(udev, mode == EM28XX_ANALOG_MODE ? dev->analog_ep_isoc : dev->dvb_ep_isoc); - usb_fill_int_urb(urb, udev, pipe, - usb_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - urb->transfer_flags = URB_ISO_ASAP | - URB_NO_TRANSFER_DMA_MAP; + usb_fill_int_urb(urb, udev, pipe, usb_bufs->buf[i], + sb_size, em28xx_irq_callback, dev, 1); + urb->transfer_flags |= URB_ISO_ASAP; k = 0; for (j = 0; j < usb_bufs->num_packets; j++) { urb->iso_frame_desc[j].offset = k; @@ -1005,8 +1015,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, int rc; int alloc; - em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n", - mode); + em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode); dev->usb_ctl.urb_data_copy = urb_data_copy; @@ -1077,7 +1086,11 @@ int em28xx_register_extension(struct em28xx_ops *ops) mutex_lock(&em28xx_devlist_mutex); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->init(dev); + if (ops->init) { + ops->init(dev); + if (dev->dev_next) + ops->init(dev->dev_next); + } } mutex_unlock(&em28xx_devlist_mutex); pr_info("em28xx: Registered (%s) extension\n", ops->name); @@ -1091,7 +1104,11 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->fini(dev); + if (ops->fini) { + if (dev->dev_next) + ops->fini(dev->dev_next); + ops->fini(dev); + } } list_del(&ops->next); mutex_unlock(&em28xx_devlist_mutex); @@ -1106,8 +1123,11 @@ void em28xx_init_extension(struct em28xx *dev) mutex_lock(&em28xx_devlist_mutex); list_add_tail(&dev->devlist, &em28xx_devlist); list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->init) + if (ops->init) { ops->init(dev); + if (dev->dev_next) + ops->init(dev->dev_next); + } } mutex_unlock(&em28xx_devlist_mutex); } @@ -1118,8 +1138,11 @@ void em28xx_close_extension(struct em28xx *dev) mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->fini) + if (ops->fini) { + if (dev->dev_next) + ops->fini(dev->dev_next); ops->fini(dev); + } } list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); @@ -1134,6 +1157,8 @@ int em28xx_suspend_extension(struct em28xx *dev) list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->suspend) ops->suspend(dev); + if (dev->dev_next) + ops->suspend(dev->dev_next); } mutex_unlock(&em28xx_devlist_mutex); return 0; @@ -1148,6 +1173,8 @@ int em28xx_resume_extension(struct em28xx *dev) list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->resume) ops->resume(dev); + if (dev->dev_next) + ops->resume(dev->dev_next); } mutex_unlock(&em28xx_devlist_mutex); return 0; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 8a81c94a8a27..a54cb8dc52c9 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1,25 +1,25 @@ -/* - DVB device driver for em28xx - - (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org> - - (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> - - Fixes for the driver to properly work with HVR-950 - - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick - - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 - - (c) 2008 Aidan Thornton <makosoft@googlemail.com> - - (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> - - Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: - (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> - (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] - - 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// DVB device driver for em28xx +// +// (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org> +// +// (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> +// - Fixes for the driver to properly work with HVR-950 +// - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick +// - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 +// +// (c) 2008 Aidan Thornton <makosoft@googlemail.com> +// +// (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: +// (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> +// (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] +// +// 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 version 2 of the License. #include "em28xx.h" @@ -64,7 +64,7 @@ #include "qm1d1c0042.h" MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface"); MODULE_VERSION(EM28XX_VERSION); @@ -96,7 +96,7 @@ struct em28xx_dvb { struct dvb_net net; /* Due to DRX-K - probably need changes */ - int (*gate_ctrl)(struct dvb_frontend *, int); + int (*gate_ctrl)(struct dvb_frontend *fe, int gate); struct semaphore pll_mutex; bool dont_attach_fe1; int lna_gpio; @@ -199,7 +199,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) int rc; struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; struct em28xx *dev = i2c_bus->dev; - struct usb_device *udev = interface_to_usbdev(dev->intf); int dvb_max_packet_size, packet_multiplier, dvb_alt; if (dev->dvb_xfer_bulk) { @@ -218,7 +217,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(udev, dev->ifnum, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; @@ -278,14 +276,13 @@ static int em28xx_stop_feed(struct dvb_demux_feed *feed) mutex_lock(&dvb->lock); dvb->nfeeds--; - if (0 == dvb->nfeeds) + if (!dvb->nfeeds) err = em28xx_stop_streaming(dvb); mutex_unlock(&dvb->lock); return err; } - /* ------------------------------------------------------------------ */ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) { @@ -358,7 +355,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .gpio = S5H1409_GPIO_OFF, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK + .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK }; static struct tda18271_std_map kworld_a340_std_map = { @@ -514,14 +511,15 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) em28xx_gpio_set(dev, hauppauge_hvr930c_init); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(10); + usleep_range(10000, 11000); dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + regs[i].r, regs[i].len); em28xx_gpio_set(dev, hauppauge_hvr930c_end); msleep(100); @@ -530,8 +528,7 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) msleep(30); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); - msleep(10); - + usleep_range(10000, 11000); } static void terratec_h5_init(struct em28xx *dev) @@ -571,14 +568,15 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_h5_init); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); - msleep(10); + usleep_range(10000, 11000); dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_h5_end); }; @@ -624,14 +622,15 @@ static void terratec_htc_stick_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_htc_stick_init); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(10); + usleep_range(10000, 11000); dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_htc_stick_end); }; @@ -682,14 +681,15 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_htc_usb_xs_init); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); + usleep_range(10000, 11000); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(10); + usleep_range(10000, 11000); dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + regs[i].r, regs[i].len); em28xx_gpio_set(dev, terratec_htc_usb_xs_end); }; @@ -718,7 +718,8 @@ static void pctv_520e_init(struct em28xx *dev) dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; /* 0x41 */ for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); + i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], + regs[i].r, regs[i].len); }; static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe) @@ -780,7 +781,7 @@ static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) static u8 tuner_go[] = { TUNER_GO, 0x01}; mt352_write(fe, clock_config, sizeof(clock_config)); - udelay(200); + usleep_range(200, 250); mt352_write(fe, reset, sizeof(reset)); mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); mt352_write(fe, agc_cfg, sizeof(agc_cfg)); @@ -840,8 +841,8 @@ static void px_bcud_init(struct em28xx *dev) /* sleeping ISDB-T */ dev->dvb->i2c_client_demod->addr = 0x14; for (i = 0; i < ARRAY_SIZE(regs1); i++) - i2c_master_send(dev->dvb->i2c_client_demod, regs1[i].r, - regs1[i].len); + i2c_master_send(dev->dvb->i2c_client_demod, + regs1[i].r, regs1[i].len); /* sleeping ISDB-S */ dev->dvb->i2c_client_demod->addr = 0x15; for (i = 0; i < ARRAY_SIZE(regs2); i++) @@ -934,7 +935,7 @@ static struct lgdt3306a_config hauppauge_01595_lgdt3306a_config = { /* ------------------------------------------------------------------ */ -static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) +static noinline_for_stack int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) { struct dvb_frontend *fe; struct xc2028_config cfg; @@ -1077,7 +1078,7 @@ static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); /* If the analog part won't create RF connectors, DVB will do it */ - if (!dev->has_video || (dev->tuner_type == TUNER_ABSENT)) + if (!dev->has_video || dev->tuner_type == TUNER_ABSENT) create_rf_connector = true; result = dvb_create_media_graph(&dvb->adapter, create_rf_connector); @@ -1126,10 +1127,285 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) dvb_unregister_adapter(&dvb->adapter); } +static int em28174_dvb_init_pctv_460e(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct tda10071_platform_data tda10071_pdata = {}; + struct a8293_platform_data a8293_pdata = {}; + + /* attach demod + tuner combo */ + tda10071_pdata.clk = 40444000; /* 40.444 MHz */ + tda10071_pdata.i2c_wr_max = 64; + tda10071_pdata.ts_mode = TDA10071_TS_SERIAL; + tda10071_pdata.pll_multiplier = 20; + tda10071_pdata.tuner_i2c_addr = 0x14; + + dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118", + &dev->i2c_adap[dev->def_i2c_bus], + 0x55, &tda10071_pdata); + if (!dvb->i2c_client_demod) + return -ENODEV; + + dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod); + + /* attach SEC */ + a8293_pdata.dvb_frontend = dvb->fe[0]; + + dvb->i2c_client_sec = dvb_module_probe("a8293", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x08, &a8293_pdata); + if (!dvb->i2c_client_sec) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + +static int em28178_dvb_init_pctv_461e(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *i2c_adapter; + struct m88ds3103_platform_data m88ds3103_pdata = {}; + struct ts2020_config ts2020_config = {}; + struct a8293_platform_data a8293_pdata = {}; + + /* attach demod */ + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL; + m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk_pol = 1; + m88ds3103_pdata.agc = 0x99; + + dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x68, &m88ds3103_pdata); + if (!dvb->i2c_client_demod) + return -ENODEV; + + dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod); + + /* attach tuner */ + ts2020_config.fe = dvb->fe[0]; + + dvb->i2c_client_tuner = dvb_module_probe("ts2020", "ts2022", + i2c_adapter, + 0x60, &ts2020_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + /* delegate signal strength measurement to tuner */ + dvb->fe[0]->ops.read_signal_strength = + dvb->fe[0]->ops.tuner_ops.get_rf_strength; + + /* attach SEC */ + a8293_pdata.dvb_frontend = dvb->fe[0]; + dvb->i2c_client_sec = dvb_module_probe("a8293", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x08, &a8293_pdata); + if (!dvb->i2c_client_sec) { + dvb_module_release(dvb->i2c_client_tuner); + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + +static int em28178_dvb_init_pctv_292e(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + + /* attach demod */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + si2168_config.spectral_inversion = true; + + dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x64, &si2168_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL, + adapter, + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna; + + return 0; +} + +static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + + /* attach demod */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + + dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x64, &si2168_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 0; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2146", + adapter, + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + +static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct tc90522_config tc90522_config = {}; + struct qm1d1c0042_config qm1d1c0042_config = {}; + + /* attach demod */ + dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat", + &dev->i2c_adap[dev->def_i2c_bus], + 0x15, &tc90522_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + /* attach tuner */ + qm1d1c0042_config.fe = tc90522_config.fe; + qm1d1c0042_config.lpf = 1; + + dvb->i2c_client_tuner = dvb_module_probe("qm1d1c0042", NULL, + tc90522_config.tuner_i2c, + 0x61, &qm1d1c0042_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + dvb->fe[0] = tc90522_config.fe; + px_bcud_init(dev); + + return 0; +} + +static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + unsigned char addr; + + /* attach demod */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.spectral_inversion = true; + addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67; + + dvb->i2c_client_demod = dvb_module_probe("si2168", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + addr, &si2168_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63; + + dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL, + adapter, + addr, &si2157_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + +static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *adapter; + struct lgdt3306a_config lgdt3306a_config = {}; + struct si2157_config si2157_config = {}; + unsigned char addr; + + /* attach demod */ + lgdt3306a_config = hauppauge_01595_lgdt3306a_config; + lgdt3306a_config.fe = &dvb->fe[0]; + lgdt3306a_config.i2c_adapter = &adapter; + addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e; + + dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + addr, &lgdt3306a_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; + si2157_config.inversion = 1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x62; + + dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL, + adapter, + 0x60, &si2157_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + static int em28xx_dvb_init(struct em28xx *dev) { - int result = 0; + int result = 0, dvb_alt = 0; struct em28xx_dvb *dvb; + struct usb_device *udev; if (dev->is_audio_only) { /* Shouldn't initialize IR for this interface */ @@ -1143,12 +1419,13 @@ static int em28xx_dvb_init(struct em28xx *dev) dev_info(&dev->intf->dev, "Binding DVB extension\n"); - dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); + dvb = kzalloc(sizeof(*dvb), GFP_KERNEL); if (!dvb) return -ENOMEM; dev->dvb = dvb; - dvb->fe[0] = dvb->fe[1] = NULL; + dvb->fe[0] = NULL; + dvb->fe[1] = NULL; /* pre-allocate DVB usb transfer buffers */ if (dev->dvb_xfer_bulk) { @@ -1178,7 +1455,8 @@ static int em28xx_dvb_init(struct em28xx *dev) switch (dev->model) { case EM2874_BOARD_LEADERSHIP_ISDBT: dvb->fe[0] = dvb_attach(s921_attach, - &sharp_isdbt, &dev->i2c_adap[dev->def_i2c_bus]); + &sharp_isdbt, + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; @@ -1191,8 +1469,8 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->fe[0] = dvb_attach(lgdt330x_attach, - &em2880_lgdt3303_dev, - &dev->i2c_adap[dev->def_i2c_bus]); + &em2880_lgdt3303_dev, + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1200,8 +1478,8 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2880_BOARD_KWORLD_DVB_310U: dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_with_xc3028, - &dev->i2c_adap[dev->def_i2c_bus]); + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1211,8 +1489,8 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap[dev->def_i2c_bus]); + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1223,16 +1501,17 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2881_BOARD_PINNACLE_HYBRID_PRO: case EM2882_BOARD_DIKOM_DK300: case EM2882_BOARD_KWORLD_VS_DVBT: + /* + * Those boards could have either a zl10353 or a mt352. + * If the chip id isn't for zl10353, try mt352. + */ dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] == NULL) { - /* This board could have either a zl10353 or a mt352. - If the chip id isn't for zl10353, try mt352 */ + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap[dev->def_i2c_bus]); + if (!dvb->fe[0]) dvb->fe[0] = dvb_attach(mt352_attach, - &terratec_xs_mt352_cfg, - &dev->i2c_adap[dev->def_i2c_bus]); - } + &terratec_xs_mt352_cfg, + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; @@ -1241,27 +1520,28 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2870_BOARD_TERRATEC_XS_MT2060: dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_no_i2c_gate_dev, - &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) { + &em28xx_zl10353_no_i2c_gate_dev, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0]) { dvb_attach(mt2060_attach, dvb->fe[0], - &dev->i2c_adap[dev->def_i2c_bus], - &em28xx_mt2060_config, 1220); + &dev->i2c_adap[dev->def_i2c_bus], + &em28xx_mt2060_config, 1220); } break; case EM2870_BOARD_KWORLD_355U: dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_no_i2c_gate_dev, - &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) + &em28xx_zl10353_no_i2c_gate_dev, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0]) dvb_attach(qt1010_attach, dvb->fe[0], - &dev->i2c_adap[dev->def_i2c_bus], &em28xx_qt1010_config); + &dev->i2c_adap[dev->def_i2c_bus], + &em28xx_qt1010_config); break; case EM2883_BOARD_KWORLD_HYBRID_330U: case EM2882_BOARD_EVGA_INDTUBE: dvb->fe[0] = dvb_attach(s5h1409_attach, - &em28xx_s5h1409_with_xc3028, - &dev->i2c_adap[dev->def_i2c_bus]); + &em28xx_s5h1409_with_xc3028, + &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; @@ -1269,9 +1549,9 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2882_BOARD_KWORLD_ATSC_315U: dvb->fe[0] = dvb_attach(lgdt330x_attach, - &em2880_lgdt3303_dev, - &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) { + &em2880_lgdt3303_dev, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0]) { if (!dvb_attach(simple_tuner_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x61, TUNER_THOMSON_DTT761X)) { @@ -1293,8 +1573,9 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2870_BOARD_REDDO_DVB_C_USB_BOX: /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ dvb->fe[0] = dvb_attach(tda10023_attach, - &em28xx_tda10023_config, - &dev->i2c_adap[dev->def_i2c_bus], 0x48); + &em28xx_tda10023_config, + &dev->i2c_adap[dev->def_i2c_bus], + 0x48); if (dvb->fe[0]) { if (!dvb_attach(simple_tuner_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], @@ -1306,18 +1587,18 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2870_BOARD_KWORLD_A340: dvb->fe[0] = dvb_attach(lgdt3305_attach, - &em2870_lgdt3304_dev, - &dev->i2c_adap[dev->def_i2c_bus]); + &em2870_lgdt3304_dev, + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; } if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, &dev->i2c_adap[dev->def_i2c_bus], - &kworld_a340_config)) { - dvb_frontend_detach(dvb->fe[0]); - result = -EINVAL; - goto out_free; + &kworld_a340_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; } break; case EM28174_BOARD_PCTV_290E: @@ -1335,7 +1616,6 @@ static int em28xx_dvb_init(struct em28xx *dev) 0x60, &dev->i2c_adap[dev->def_i2c_bus], &em28xx_cxd2820r_tda18271_config)) { - dvb_frontend_detach(dvb->fe[0]); result = -EINVAL; goto out_free; @@ -1360,12 +1640,13 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: { - struct xc5000_config cfg; + struct xc5000_config cfg = {}; hauppauge_hvr930c_init(dev); dvb->fe[0] = dvb_attach(drxk_attach, - &hauppauge_930c_drxk, &dev->i2c_adap[dev->def_i2c_bus]); + &hauppauge_930c_drxk, + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1377,14 +1658,13 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; /* Attach xc5000 */ - memset(&cfg, 0, sizeof(cfg)); cfg.i2c_address = 0x61; cfg.if_khz = 4000; if (dvb->fe[0]->ops.i2c_gate_ctrl) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], - &cfg)) { + if (!dvb_attach(xc5000_attach, dvb->fe[0], + &dev->i2c_adap[dev->def_i2c_bus], &cfg)) { result = -EINVAL; goto out_free; } @@ -1396,7 +1676,8 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2884_BOARD_TERRATEC_H5: terratec_h5_init(dev); - dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap[dev->def_i2c_bus]); + dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, + &dev->i2c_adap[dev->def_i2c_bus]); if (!dvb->fe[0]) { result = -EINVAL; goto out_free; @@ -1410,7 +1691,8 @@ static int em28xx_dvb_init(struct em28xx *dev) /* Attach tda18271 to DVB-C frontend */ if (dvb->fe[0]->ops.i2c_gate_ctrl) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x60)) { + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], + &dev->i2c_adap[dev->def_i2c_bus], 0x60)) { result = -EINVAL; goto out_free; } @@ -1420,72 +1702,23 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2884_BOARD_C3TECH_DIGITAL_DUO: dvb->fe[0] = dvb_attach(mb86a20s_attach, - &c3tech_duo_mb86a20s_config, - &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) + &c3tech_duo_mb86a20s_config, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0]) dvb_attach(tda18271_attach, dvb->fe[0], 0x60, &dev->i2c_adap[dev->def_i2c_bus], &c3tech_duo_tda18271_config); break; - case EM28174_BOARD_PCTV_460E: { - struct i2c_client *client; - struct i2c_board_info board_info; - struct tda10071_platform_data tda10071_pdata = {}; - struct a8293_platform_data a8293_pdata = {}; - - /* attach demod + tuner combo */ - tda10071_pdata.clk = 40444000, /* 40.444 MHz */ - tda10071_pdata.i2c_wr_max = 64, - tda10071_pdata.ts_mode = TDA10071_TS_SERIAL, - tda10071_pdata.pll_multiplier = 20, - tda10071_pdata.tuner_i2c_addr = 0x14, - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "tda10071_cx24118", I2C_NAME_SIZE); - board_info.addr = 0x55; - board_info.platform_data = &tda10071_pdata; - request_module("tda10071"); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; + case EM28174_BOARD_PCTV_460E: + result = em28174_dvb_init_pctv_460e(dev); + if (result) goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - dvb->fe[0] = tda10071_pdata.get_dvb_frontend(client); - dvb->i2c_client_demod = client; - - /* attach SEC */ - a8293_pdata.dvb_frontend = dvb->fe[0]; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "a8293", I2C_NAME_SIZE); - board_info.addr = 0x08; - board_info.platform_data = &a8293_pdata; - request_module("a8293"); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - dvb->i2c_client_sec = client; break; - } case EM2874_BOARD_DELOCK_61959: case EM2874_BOARD_MAXMEDIA_UB425_TC: /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, - &dev->i2c_adap[dev->def_i2c_bus]); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0]) { /* disable I2C-gate */ @@ -1507,7 +1740,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach demodulator */ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, - &dev->i2c_adap[dev->def_i2c_bus]); + &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0]) { /* attach tuner */ @@ -1579,13 +1812,7 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2874_BOARD_KWORLD_UB435Q_V3: { - struct i2c_client *client; struct i2c_adapter *adapter = &dev->i2c_adap[dev->def_i2c_bus]; - struct i2c_board_info board_info = { - .type = "tda18212", - .addr = 0x60, - .platform_data = &kworld_ub435q_v3_config, - }; dvb->fe[0] = dvb_attach(lgdt3305_attach, &em2874_lgdt3305_nogate_dev, @@ -1597,28 +1824,23 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach tuner */ kworld_ub435q_v3_config.fe = dvb->fe[0]; - request_module("tda18212"); - client = i2c_new_device(adapter, &board_info); - if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dvb->fe[0]); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); + dvb->i2c_client_tuner = dvb_module_probe("tda18212", NULL, + adapter, 0x60, + &kworld_ub435q_v3_config); + if (!dvb->i2c_client_tuner) { dvb_frontend_detach(dvb->fe[0]); result = -ENODEV; goto out_free; } - - dvb->i2c_client_tuner = client; break; } case EM2874_BOARD_PCTV_HD_MINI_80E: - dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) { - dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + dvb->fe[0] = dvb_attach(drx39xxj_attach, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0]) { + dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0], + 0x60, &dev->i2c_adap[dev->def_i2c_bus], &pinnacle_80e_dvb_config); if (!dvb->fe[0]) { @@ -1627,410 +1849,42 @@ static int em28xx_dvb_init(struct em28xx *dev) } } break; - case EM28178_BOARD_PCTV_461E: { - struct i2c_client *client; - struct i2c_adapter *i2c_adapter; - struct i2c_board_info board_info; - struct m88ds3103_platform_data m88ds3103_pdata = {}; - struct ts2020_config ts2020_config = {}; - struct a8293_platform_data a8293_pdata = {}; - - /* attach demod */ - m88ds3103_pdata.clk = 27000000; - m88ds3103_pdata.i2c_wr_max = 33; - m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL; - m88ds3103_pdata.ts_clk = 16000; - m88ds3103_pdata.ts_clk_pol = 1; - m88ds3103_pdata.agc = 0x99; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); - board_info.addr = 0x68; - board_info.platform_data = &m88ds3103_pdata; - request_module("m88ds3103"); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(client); - i2c_adapter = m88ds3103_pdata.get_i2c_adapter(client); - dvb->i2c_client_demod = client; - - /* attach tuner */ - ts2020_config.fe = dvb->fe[0]; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "ts2022", I2C_NAME_SIZE); - board_info.addr = 0x60; - board_info.platform_data = &ts2020_config; - request_module("ts2020"); - client = i2c_new_device(i2c_adapter, &board_info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - dvb->i2c_client_tuner = client; - /* delegate signal strength measurement to tuner */ - dvb->fe[0]->ops.read_signal_strength = - dvb->fe[0]->ops.tuner_ops.get_rf_strength; - - /* attach SEC */ - a8293_pdata.dvb_frontend = dvb->fe[0]; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "a8293", I2C_NAME_SIZE); - board_info.addr = 0x08; - board_info.platform_data = &a8293_pdata; - request_module("a8293"); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_tuner->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_tuner); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_tuner->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_tuner); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; + case EM28178_BOARD_PCTV_461E: + result = em28178_dvb_init_pctv_461e(dev); + if (result) goto out_free; - } - dvb->i2c_client_sec = client; break; - } case EM28178_BOARD_PCTV_292E: - { - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &adapter; - si2168_config.fe = &dvb->fe[0]; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - 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(info.type); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_demod = client; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = dvb->fe[0]; - si2157_config.if_port = 1; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - si2157_config.mdev = dev->media_dev; -#endif - 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(info.type); - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_tuner = client; - dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna; - } + result = em28178_dvb_init_pctv_292e(dev); + if (result) + goto out_free; break; case EM28178_BOARD_TERRATEC_T2_STICK_HD: - { - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &adapter; - si2168_config.fe = &dvb->fe[0]; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - 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(info.type); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_demod = client; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = dvb->fe[0]; - si2157_config.if_port = 0; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - si2157_config.mdev = dev->media_dev; -#endif - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2146", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - request_module("si2157"); - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_tuner = client; - } + result = em28178_dvb_init_terratec_t2_stick_hd(dev); + if (result) + goto out_free; break; - case EM28178_BOARD_PLEX_PX_BCUD: - { - struct i2c_client *client; - struct i2c_board_info info; - struct tc90522_config tc90522_config; - struct qm1d1c0042_config qm1d1c0042_config; - - /* attach demod */ - memset(&tc90522_config, 0, sizeof(tc90522_config)); - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "tc90522sat", I2C_NAME_SIZE); - info.addr = 0x15; - info.platform_data = &tc90522_config; - request_module("tc90522"); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - dvb->i2c_client_demod = client; - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - - /* attach tuner */ - memset(&qm1d1c0042_config, 0, - sizeof(qm1d1c0042_config)); - qm1d1c0042_config.fe = tc90522_config.fe; - qm1d1c0042_config.lpf = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "qm1d1c0042", I2C_NAME_SIZE); - info.addr = 0x61; - info.platform_data = &qm1d1c0042_config; - request_module(info.type); - client = i2c_new_device(tc90522_config.tuner_i2c, - &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - dvb->i2c_client_tuner = client; - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - dvb->fe[0] = tc90522_config.fe; - px_bcud_init(dev); - } + result = em28178_dvb_init_plex_px_bcud(dev); + if (result) + goto out_free; break; case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB: - { - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &adapter; - si2168_config.fe = &dvb->fe[0]; - 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(info.type); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_demod = client; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = dvb->fe[0]; - si2157_config.if_port = 1; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - si2157_config.mdev = dev->media_dev; -#endif - 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(info.type); - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_tuner = client; - - } + result = em28174_dvb_init_hauppauge_wintv_dualhd_dvb(dev); + if (result) + goto out_free; break; case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595: - { - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info = {}; - struct lgdt3306a_config lgdt3306a_config; - struct si2157_config si2157_config = {}; - - /* attach demod */ - lgdt3306a_config = hauppauge_01595_lgdt3306a_config; - lgdt3306a_config.fe = &dvb->fe[0]; - lgdt3306a_config.i2c_adapter = &adapter; - strlcpy(info.type, "lgdt3306a", sizeof(info.type)); - info.addr = 0x59; - info.platform_data = &lgdt3306a_config; - request_module(info.type); - client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], - &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_demod = client; - - /* attach tuner */ - si2157_config.fe = dvb->fe[0]; - si2157_config.if_port = 1; - si2157_config.inversion = 1; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - si2157_config.mdev = dev->media_dev; -#endif - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", sizeof(info.type)); - info.addr = 0x60; - info.platform_data = &si2157_config; - request_module(info.type); - - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod); - result = -ENODEV; - goto out_free; - } - - dvb->i2c_client_tuner = client; - } + result = em28174_dvb_init_hauppauge_wintv_dualhd_01595(dev); + if (result) + goto out_free; break; default: dev_err(&dev->intf->dev, "The frontend of your DVB/ATSC card isn't supported yet\n"); break; } - if (NULL == dvb->fe[0]) { + if (!dvb->fe[0]) { dev_err(&dev->intf->dev, "frontend initialization failed\n"); result = -EINVAL; goto out_free; @@ -2046,6 +1900,14 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result < 0) goto out_free; + if (dev->dvb_xfer_bulk) { + dvb_alt = 0; + } else { /* isoc */ + dvb_alt = dev->dvb_alt_isoc; + } + + udev = interface_to_usbdev(dev->intf); + usb_set_interface(udev, dev->ifnum, dvb_alt); dev_info(&dev->intf->dev, "DVB extension successfully initialized\n"); kref_get(&dev->ref); @@ -2071,7 +1933,6 @@ static inline void prevent_sleep(struct dvb_frontend_ops *ops) static int em28xx_dvb_fini(struct em28xx *dev) { struct em28xx_dvb *dvb; - struct i2c_client *client; if (dev->is_audio_only) { /* Shouldn't initialize IR for this interface */ @@ -2093,8 +1954,10 @@ static int em28xx_dvb_fini(struct em28xx *dev) em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); if (dev->disconnected) { - /* We cannot tell the device to sleep - * once it has been unplugged. */ + /* + * We cannot tell the device to sleep + * once it has been unplugged. + */ if (dvb->fe[0]) { prevent_sleep(&dvb->fe[0]->ops); dvb->fe[0]->exit = DVB_FE_DEVICE_REMOVED; @@ -2107,26 +1970,10 @@ static int em28xx_dvb_fini(struct em28xx *dev) em28xx_unregister_dvb(dvb); - /* remove I2C SEC */ - client = dvb->i2c_client_sec; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - /* remove I2C tuner */ - client = dvb->i2c_client_tuner; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - /* remove I2C demod */ - client = dvb->i2c_client_demod; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } + /* release I2C module bindings */ + dvb_module_release(dvb->i2c_client_sec); + dvb_module_release(dvb->i2c_client_tuner); + dvb_module_release(dvb->i2c_client_demod); kfree(dvb); dev->dvb = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 9bf49d666e5a..9151bccd859a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -1,26 +1,22 @@ -/* - em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> - Markus Rechberger <mrechberger@gmail.com> - Mauro Carvalho Chehab <mchehab@infradead.org> - Sascha Sommer <saschasommer@freenet.de> - Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices +// +// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> +// Markus Rechberger <mrechberger@gmail.com> +// Mauro Carvalho Chehab <mchehab@infradead.org> +// Sascha Sommer <saschasommer@freenet.de> +// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// 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 "em28xx.h" @@ -50,6 +46,35 @@ MODULE_PARM_DESC(i2c_debug, "i2c debug message level (1: normal debug, 2: show I "i2c: %s: " fmt, __func__, ## arg); \ } while (0) +/* + * Time in msecs to wait for i2c xfers to finish. + * 35ms is the maximum time a SMBUS device could wait when + * clock stretching is used. As the transfer itself will take + * some time to happen, set it to 35 ms. + * + * Ok, I2C doesn't specify any limit. So, eventually, we may need + * to increase this timeout. + */ +#define EM28XX_I2C_XFER_TIMEOUT 35 /* ms */ + +static int em28xx_i2c_timeout(struct em28xx *dev) +{ + int time = EM28XX_I2C_XFER_TIMEOUT; + + switch (dev->i2c_speed & 0x03) { + case EM28XX_I2C_FREQ_25_KHZ: + time += 4; /* Assume 4 ms for transfers */ + break; + case EM28XX_I2C_FREQ_100_KHZ: + case EM28XX_I2C_FREQ_400_KHZ: + time += 1; /* Assume 1 ms for transfers */ + break; + default: /* EM28XX_I2C_FREQ_1_5_MHZ */ + break; + } + + return msecs_to_jiffies(time); +} /* * em2800_i2c_send_bytes() @@ -57,14 +82,13 @@ MODULE_PARM_DESC(i2c_debug, "i2c debug message level (1: normal debug, 2: show I */ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + em28xx_i2c_timeout(dev); int ret; u8 b2[6]; if (len < 1 || len > 4) return -EOPNOTSUPP; - BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; b2[3] = buf[0]; @@ -98,7 +122,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) ret); return ret; } - msleep(5); + usleep_range(5000, 6000); } dprintk(0, "write to i2c device at 0x%x timed out\n", addr); return -ETIMEDOUT; @@ -110,7 +134,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) */ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + em28xx_i2c_timeout(dev); u8 buf2[4]; int ret; int i; @@ -145,14 +169,13 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) ret); return ret; } - msleep(5); + usleep_range(5000, 6000); } - if (ret != 0x84 + len - 1) { + if (ret != 0x84 + len - 1) dprintk(0, "read from i2c device at 0x%x timed out\n", addr); - } /* get the received message */ - ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); + ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4 - len, buf2, len); if (ret != len) { dev_warn(&dev->intf->dev, "reading from i2c device at 0x%x failed: couldn't get the received message from the bridge (error=%i)\n", @@ -186,7 +209,7 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len, int stop) { - unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT); + unsigned long timeout = jiffies + em28xx_i2c_timeout(dev); int ret; if (len < 1 || len > 64) @@ -204,12 +227,11 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, "writing to i2c device at 0x%x failed (error=%i)\n", addr, ret); return ret; - } else { - dev_warn(&dev->intf->dev, - "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", - len, addr, ret); - return -EIO; } + dev_warn(&dev->intf->dev, + "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", + len, addr, ret); + return -EIO; } /* wait for completion */ @@ -228,7 +250,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, ret); return ret; } - msleep(5); + usleep_range(5000, 6000); /* * NOTE: do we really have to wait for success ? * Never seen anything else than 0x00 or 0x10 @@ -351,12 +373,12 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, "writing to i2c device at 0x%x failed (error=%i)\n", addr, ret); return ret; - } else { - dev_warn(&dev->intf->dev, - "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", - len, addr, ret); - return -EIO; } + + dev_warn(&dev->intf->dev, + "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n", + len, addr, ret); + return -EIO; } /* Check success */ ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000); @@ -366,7 +388,8 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, */ if (!ret) return len; - else if (ret > 0) { + + if (ret > 0) { dprintk(1, "Bus B R08 returned 0x%02x: I2C ACK error\n", ret); return -ENXIO; } @@ -420,7 +443,8 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, */ if (!ret) return len; - else if (ret > 0) { + + if (ret > 0) { dprintk(1, "Bus B R08 returned 0x%02x: I2C ACK error\n", ret); return -ENXIO; } @@ -508,13 +532,15 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, { struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; struct em28xx *dev = i2c_bus->dev; - unsigned bus = i2c_bus->bus; + unsigned int bus = i2c_bus->bus; int addr, rc, i; u8 reg; - /* prevent i2c xfer attempts after device is disconnected - some fe's try to do i2c writes/reads from their release - interfaces when called in disconnect path */ + /* + * prevent i2c xfer attempts after device is disconnected + * some fe's try to do i2c writes/reads from their release + * interfaces when called in disconnect path + */ if (dev->disconnected) return -ENODEV; @@ -597,12 +623,13 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) if (len == length) { c = (char)len; len = -1; - } else + } else { c = *buf++; + } l = (l << 8) | c; len++; if ((len & (32 / 8 - 1)) == 0) - hash = ((hash^l) * 0x9e370001UL); + hash = ((hash ^ l) * 0x9e370001UL); } while (len); return (hash >> (32 - bits)) & 0xffffffffUL; @@ -612,7 +639,7 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) * Helper function to read data blocks from i2c clients with 8 or 16 bit * address width, 8 bit register width and auto incrementation been activated */ -static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr, +static int em28xx_i2c_read_block(struct em28xx *dev, unsigned int bus, u16 addr, bool addr_w16, u16 len, u8 *data) { int remain = len, rsize, rsize_max, ret; @@ -624,7 +651,8 @@ static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr, /* Select address */ buf[0] = addr >> 8; buf[1] = addr & 0xff; - ret = i2c_master_send(&dev->i2c_client[bus], buf + !addr_w16, 1 + addr_w16); + ret = i2c_master_send(&dev->i2c_client[bus], + buf + !addr_w16, 1 + addr_w16); if (ret < 0) return ret; /* Read data */ @@ -649,7 +677,7 @@ static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr, return len; } -static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, +static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned int bus, u8 **eedata, u16 *eedata_len) { const u16 len = 256; @@ -677,7 +705,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, } data = kzalloc(len, GFP_KERNEL); - if (data == NULL) + if (!data) return -ENOMEM; /* Read EEPROM content */ @@ -710,8 +738,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, mc_start = (data[1] << 8) + 4; /* usually 0x0004 */ dev_info(&dev->intf->dev, - "EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n", - data[0], data[1], data[2], data[3], dev->hash); + "EEPROM ID = %4ph, EEPROM hash = 0x%08lx\n", + data, dev->hash); dev_info(&dev->intf->dev, "EEPROM info:\n"); dev_info(&dev->intf->dev, @@ -769,15 +797,18 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, return 0; } - /* TODO: decrypt eeprom data for camera bridges (em25xx, em276x+) */ + /* + * TODO: decrypt eeprom data for camera bridges + * (em25xx, em276x+) + */ } else if (!dev->eeprom_addrwidth_16bit && data[0] == 0x1a && data[1] == 0xeb && data[2] == 0x67 && data[3] == 0x95) { dev->hash = em28xx_hash_mem(data, len, 32); dev_info(&dev->intf->dev, - "EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n", - data[0], data[1], data[2], data[3], dev->hash); + "EEPROM ID = %4ph, EEPROM hash = 0x%08lx\n", + data, dev->hash); dev_info(&dev->intf->dev, "EEPROM info:\n"); } else { @@ -859,8 +890,8 @@ static u32 functionality(struct i2c_adapter *i2c_adap) { struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data; - if ((i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) || - (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B)) { + if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX || + i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) { return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) & @@ -893,7 +924,7 @@ static const struct i2c_client em28xx_client_template = { * incomplete list of known devices */ static char *i2c_devs[128] = { - [0x1c >> 1] = "lgdt330x", + [0x1c >> 1] = "lgdt330x", [0x3e >> 1] = "remote IR sensor", [0x4a >> 1] = "saa7113h", [0x52 >> 1] = "drxk", @@ -916,7 +947,7 @@ static char *i2c_devs[128] = { * do_i2c_scan() * check i2c address range for devices */ -void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus) +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus) { u8 i2c_devicelist[128]; unsigned char buf; @@ -944,13 +975,14 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus) * em28xx_i2c_register() * register i2c bus */ -int em28xx_i2c_register(struct em28xx *dev, unsigned bus, +int em28xx_i2c_register(struct em28xx *dev, unsigned int bus, enum em28xx_i2c_algo_type algo_type) { int retval; - BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); - BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); + if (WARN_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg || + !dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req)) + return -ENODEV; if (bus >= NUM_I2C_BUSES) return -ENODEV; @@ -977,8 +1009,9 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus, /* Up to now, all eeproms are at bus 0 */ if (!bus) { - retval = em28xx_i2c_eeprom(dev, bus, &dev->eedata, &dev->eedata_len); - if ((retval < 0) && (retval != -ENODEV)) { + retval = em28xx_i2c_eeprom(dev, bus, + &dev->eedata, &dev->eedata_len); + if (retval < 0 && retval != -ENODEV) { dev_err(&dev->intf->dev, "%s: em28xx_i2_eeprom failed! retval [%d]\n", __func__, retval); @@ -995,7 +1028,7 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus, * em28xx_i2c_unregister() * unregister i2c_bus */ -int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus) +int em28xx_i2c_unregister(struct em28xx *dev, unsigned int bus) { if (bus >= NUM_I2C_BUSES) return -ENODEV; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 046223de1e91..2dc1be00b8b8 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -1,25 +1,21 @@ -/* - handle em28xx IR remotes via linux kernel input layer. - - Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> - Markus Rechberger <mrechberger@gmail.com> - Mauro Carvalho Chehab <mchehab@infradead.org> - Sascha Sommer <saschasommer@freenet.de> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// handle em28xx IR remotes via linux kernel input layer. +// +// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> +// Markus Rechberger <mrechberger@gmail.com> +// Mauro Carvalho Chehab <mchehab@infradead.org> +// Sascha Sommer <saschasommer@freenet.de> +// +// 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 "em28xx.h" @@ -41,15 +37,15 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); #define MODULE_NAME "em28xx" -#define dprintk( fmt, arg...) do { \ +#define dprintk(fmt, arg...) do { \ if (ir_debug) \ dev_printk(KERN_DEBUG, &ir->dev->intf->dev, \ "input: %s: " fmt, __func__, ## arg); \ } while (0) -/********************************************************** - Polling structure used by em28xx IR's - **********************************************************/ +/* + * Polling structure used by em28xx IR's + */ struct em28xx_ir_poll_result { unsigned int toggle_bit:1; @@ -76,24 +72,31 @@ struct em28xx_IR { int (*get_key_i2c)(struct i2c_client *ir, enum rc_proto *protocol, u32 *scancode); - int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); + int (*get_key)(struct em28xx_IR *ir, struct em28xx_ir_poll_result *r); }; -/********************************************************** - I2C IR based get keycodes - should be used with ir-kbd-i2c - **********************************************************/ +/* + * I2C IR based get keycodes - should be used with ir-kbd-i2c + */ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, enum rc_proto *protocol, u32 *scancode) { + int rc; unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(i2c_dev, &b, 1)) + rc = i2c_master_recv(i2c_dev, &b, 1); + if (rc != 1) { + if (rc < 0) + return rc; return -EIO; + } - /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold down. */ + /* + * it seems that 0xFE indicates that a button is still hold + * down, while 0xff indicates that no button is hold down. + */ if (b == 0xff) return 0; @@ -145,7 +148,7 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, /* poll IR chip */ - if (3 != i2c_master_recv(i2c_dev, buf, 3)) + if (i2c_master_recv(i2c_dev, buf, 3) != 3) return -EIO; if (buf[0] != 0x00) @@ -162,18 +165,28 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, { unsigned char subaddr, keydetect, key; - struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + struct i2c_msg msg[] = { + { + .addr = i2c_dev->addr, + .flags = 0, + .buf = &subaddr, .len = 1 + }, { + .addr = i2c_dev->addr, + .flags = I2C_M_RD, + .buf = &keydetect, + .len = 1 + } + }; subaddr = 0x10; - if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) + if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2) return -EIO; if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) + if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2) return -EIO; if (key == 0x00) return 0; @@ -183,9 +196,9 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, return 1; } -/********************************************************** - Poll based get keycode functions - **********************************************************/ +/* + * Poll based get keycode functions + */ /* This is for the em2860/em2880 */ static int default_polling_getkey(struct em28xx_IR *ir, @@ -195,8 +208,9 @@ static int default_polling_getkey(struct em28xx_IR *ir, int rc; u8 msg[3] = { 0, 0, 0 }; - /* Read key toggle, brand, and key code - on registers 0x45, 0x46 and 0x47 + /* + * Read key toggle, brand, and key code + * on registers 0x45, 0x46 and 0x47 */ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, msg, sizeof(msg)); @@ -237,8 +251,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, int rc; u8 msg[5] = { 0, 0, 0, 0, 0 }; - /* Read key toggle, brand, and key code - on registers 0x51-55 + /* + * Read key toggle, brand, and key code + * on registers 0x51-55 */ rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR, msg, sizeof(msg)); @@ -294,9 +309,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, return 0; } -/********************************************************** - Polling code for em28xx - **********************************************************/ +/* + * Polling code for em28xx + */ static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { @@ -347,11 +362,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) if (ir->dev->chip_id == CHIP_ID_EM2874 || ir->dev->chip_id == CHIP_ID_EM2884) - /* The em2874 clears the readcount field every time the - register is read. The em2860/2880 datasheet says that it - is supposed to clear the readcount, but it doesn't. So with - the em2874, we are looking for a non-zero read count as - opposed to a readcount that is incrementing */ + /* + * The em2874 clears the readcount field every time the + * register is read. The em2860/2880 datasheet says + * that it is supposed to clear the readcount, but it + * doesn't. So with the em2874, we are looking for a + * non-zero read count as opposed to a readcount + * that is incrementing + */ ir->last_readcount = 0; else ir->last_readcount = poll_result.read_count; @@ -476,15 +494,18 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_proto) static int em28xx_probe_i2c_ir(struct em28xx *dev) { int i = 0; - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ + /* + * Leadtek winfast tv USBII deluxe can find a non working IR-device + * at address 0x18, so if that address is needed for another board in + * the future, please put it after 0x1f. + */ const unsigned short addr_list[] = { 0x1f, 0x30, 0x47, I2C_CLIENT_END }; while (addr_list[i] != I2C_CLIENT_END) { - if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus], addr_list[i]) == 1) + if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus], + addr_list[i]) == 1) return addr_list[i]; i++; } @@ -492,9 +513,9 @@ static int em28xx_probe_i2c_ir(struct em28xx *dev) return -ENODEV; } -/********************************************************** - Handle buttons - **********************************************************/ +/* + * Handle buttons + */ static void em28xx_query_buttons(struct work_struct *work) { @@ -515,7 +536,10 @@ static void em28xx_query_buttons(struct work_struct *work) j = 0; while (dev->board.buttons[j].role >= 0 && dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) { - struct em28xx_button *button = &dev->board.buttons[j]; + const struct em28xx_button *button; + + button = &dev->board.buttons[j]; + /* Check if button uses the current address */ if (button->reg_r != dev->button_polling_addresses[i]) { j++; @@ -618,7 +642,8 @@ static void em28xx_init_buttons(struct em28xx *dev) dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL; while (dev->board.buttons[i].role >= 0 && dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) { - struct em28xx_button *button = &dev->board.buttons[i]; + const struct em28xx_button *button = &dev->board.buttons[i]; + /* Check if polling address is already on the list */ addr_new = true; for (j = 0; j < dev->num_button_polling_addresses; j++) { @@ -649,6 +674,7 @@ static void em28xx_init_buttons(struct em28xx *dev) /* Add read address to list of polling addresses */ if (addr_new) { unsigned int index = dev->num_button_polling_addresses; + dev->button_polling_addresses[index] = button->reg_r; dev->num_button_polling_addresses++; } @@ -677,7 +703,7 @@ static void em28xx_shutdown_buttons(struct em28xx *dev) /* Clear polling addresses list */ dev->num_button_polling_addresses = 0; /* Deregister input devices */ - if (dev->sbutton_input_dev != NULL) { + if (dev->sbutton_input_dev) { dev_info(&dev->intf->dev, "Deregistering snapshot button\n"); input_unregister_device(dev->sbutton_input_dev); dev->sbutton_input_dev = NULL; @@ -714,7 +740,7 @@ static int em28xx_ir_init(struct em28xx *dev) } } - if (dev->board.ir_codes == NULL && !dev->board.has_ir_i2c) { + if (!dev->board.ir_codes && !dev->board.has_ir_i2c) { /* No remote control support */ dev_warn(&dev->intf->dev, "Remote control support is not available for this card.\n"); @@ -764,7 +790,7 @@ static int em28xx_ir_init(struct em28xx *dev) goto error; } - ir->i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + ir->i2c_client = kzalloc(sizeof(*ir->i2c_client), GFP_KERNEL); if (!ir->i2c_client) goto error; ir->i2c_client->adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; @@ -881,9 +907,11 @@ static int em28xx_ir_suspend(struct em28xx *dev) if (ir) cancel_delayed_work_sync(&ir->work); cancel_delayed_work_sync(&dev->buttons_query_work); - /* is canceling delayed work sufficient or does the rc event - kthread needs stopping? kthread is stopped in - ir_raw_event_unregister() */ + /* + * is canceling delayed work sufficient or does the rc event + * kthread needs stopping? kthread is stopped in + * ir_raw_event_unregister() + */ return 0; } @@ -895,8 +923,10 @@ static int em28xx_ir_resume(struct em28xx *dev) return 0; dev_info(&dev->intf->dev, "Resuming input extension\n"); - /* if suspend calls ir_raw_event_unregister(), the should call - ir_raw_event_register() */ + /* + * if suspend calls ir_raw_event_unregister(), the should call + * ir_raw_event_register() + */ if (ir) schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); if (dev->num_button_polling_addresses) @@ -924,7 +954,7 @@ static void __exit em28xx_rc_unregister(void) em28xx_unregister_extension(&rc_ops); } -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_DESCRIPTION(DRIVER_DESC " - input interface"); MODULE_VERSION(EM28XX_VERSION); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 9e5cdfb25a73..f53afe18e92d 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -1,17 +1,22 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#define EM_GPIO_0 (1 << 0) -#define EM_GPIO_1 (1 << 1) -#define EM_GPIO_2 (1 << 2) -#define EM_GPIO_3 (1 << 3) -#define EM_GPIO_4 (1 << 4) -#define EM_GPIO_5 (1 << 5) -#define EM_GPIO_6 (1 << 6) -#define EM_GPIO_7 (1 << 7) - -#define EM_GPO_0 (1 << 0) -#define EM_GPO_1 (1 << 1) -#define EM_GPO_2 (1 << 2) -#define EM_GPO_3 (1 << 3) + +/* + * em28xx-reg.h - Register definitions for em28xx driver + */ + +#define EM_GPIO_0 ((unsigned char)BIT(0)) +#define EM_GPIO_1 ((unsigned char)BIT(1)) +#define EM_GPIO_2 ((unsigned char)BIT(2)) +#define EM_GPIO_3 ((unsigned char)BIT(3)) +#define EM_GPIO_4 ((unsigned char)BIT(4)) +#define EM_GPIO_5 ((unsigned char)BIT(5)) +#define EM_GPIO_6 ((unsigned char)BIT(6)) +#define EM_GPIO_7 ((unsigned char)BIT(7)) + +#define EM_GPO_0 ((unsigned char)BIT(0)) +#define EM_GPO_1 ((unsigned char)BIT(1)) +#define EM_GPO_2 ((unsigned char)BIT(2)) +#define EM_GPO_3 ((unsigned char)BIT(3)) /* em28xx endpoints */ /* 0x82: (always ?) analog */ @@ -203,10 +208,11 @@ #define EM28XX_R43_AC97BUSY 0x43 #define EM28XX_R45_IR 0x45 - /* 0x45 bit 7 - parity bit - bits 6-0 - count - 0x46 IR brand - 0x47 IR data + /* + * 0x45 bit 7 - parity bit + * bits 6-0 - count + * 0x46 IR brand + * 0x47 IR data */ /* em2874 registers */ @@ -249,12 +255,12 @@ #define EM2874_IR_RC6_MODE_6A 0x0b /* em2874 Transport Stream Enable Register (0x5f) */ -#define EM2874_TS1_CAPTURE_ENABLE (1 << 0) -#define EM2874_TS1_FILTER_ENABLE (1 << 1) -#define EM2874_TS1_NULL_DISCARD (1 << 2) -#define EM2874_TS2_CAPTURE_ENABLE (1 << 4) -#define EM2874_TS2_FILTER_ENABLE (1 << 5) -#define EM2874_TS2_NULL_DISCARD (1 << 6) +#define EM2874_TS1_CAPTURE_ENABLE ((unsigned char)BIT(0)) +#define EM2874_TS1_FILTER_ENABLE ((unsigned char)BIT(1)) +#define EM2874_TS1_NULL_DISCARD ((unsigned char)BIT(2)) +#define EM2874_TS2_CAPTURE_ENABLE ((unsigned char)BIT(4)) +#define EM2874_TS2_FILTER_ENABLE ((unsigned char)BIT(5)) +#define EM2874_TS2_NULL_DISCARD ((unsigned char)BIT(6)) /* register settings */ #define EM2800_AUDIO_SRC_TUNER 0x0d diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h index 9c411aac3878..1788dbf9024a 100644 --- a/drivers/media/usb/em28xx/em28xx-v4l.h +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -1,17 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB - video capture devices - - Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.com> - - 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 version 2 of the License. - - 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. + * em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + * video capture devices + * + * Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.com> + * + * 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 version 2 of the License. + * + * 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. */ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index f5123651ef30..63c48361d3f2 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -1,25 +1,20 @@ -/* - em28xx-vbi.c - VBI driver for em28xx - - Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> - - This work was sponsored by EyeMagnet Limited. - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-vbi.c - VBI driver for em28xx +// +// Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> +// +// This work was sponsored by EyeMagnet Limited. +// +// 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 "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a2ba2d905952..d70ee13cc52e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1,30 +1,26 @@ -/* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB - video capture devices - - Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> - Markus Rechberger <mrechberger@gmail.com> - Mauro Carvalho Chehab <mchehab@infradead.org> - Sascha Sommer <saschasommer@freenet.de> - Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> - - Some parts based on SN9C10x PC Camera Controllers GPL driver made - by Luca Risolia <luca.risolia@studio.unibo.it> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB +// video capture devices +// +// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> +// Markus Rechberger <mrechberger@gmail.com> +// Mauro Carvalho Chehab <mchehab@infradead.org> +// Sascha Sommer <saschasommer@freenet.de> +// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> +// +// Some parts based on SN9C10x PC Camera Controllers GPL driver made +// by Luca Risolia <luca.risolia@studio.unibo.it> +// +// 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 "em28xx.h" @@ -77,7 +73,7 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_VERSION(EM28XX_VERSION); #define EM25XX_FRMDATAHDR_BYTE1 0x02 @@ -148,7 +144,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) { struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (dev->board.is_webcam) + if (dev->is_webcam) return v4l2->sensor_xres; if (dev->board.max_range_640_480) @@ -161,7 +157,7 @@ static inline unsigned int norm_maxh(struct em28xx *dev) { struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (dev->board.is_webcam) + if (dev->is_webcam) return v4l2->sensor_yres; if (dev->board.max_range_640_480) @@ -176,7 +172,7 @@ static int em28xx_vbi_supported(struct em28xx *dev) if (disable_vbi == 1) return 0; - if (dev->board.is_webcam) + if (dev->is_webcam) return 0; /* FIXME: check subdevices for VBI support */ @@ -250,7 +246,8 @@ static int em28xx_set_outfmt(struct em28xx *dev) if (em28xx_vbi_supported(dev) == 1) { vinctrl |= EM28XX_VINCTRL_VBI_RAW; em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); - em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, v4l2->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, + v4l2->vbi_width / 4); em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height); if (v4l2->norm & V4L2_STD_525_60) { /* NTSC */ @@ -320,8 +317,10 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) buf[0] = v; buf[1] = v >> 8; em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); - /* it seems that both H and V scalers must be active - to work correctly */ + /* + * it seems that both H and V scalers must be active + * to work correctly + */ mode = (h || v) ? 0x30 : 0x00; } return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode); @@ -345,13 +344,15 @@ static int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - /* If we don't set the start position to 2 in VBI mode, we end up - with line 20/21 being YUYV encoded instead of being in 8-bit - greyscale. The core of the issue is that line 21 (and line 23 for - PAL WSS) are inside of active video region, and as a result they - get the pixelformatting associated with that area. So by cropping - it out, we end up with the same format as the rest of the VBI - region */ + /* + * If we don't set the start position to 2 in VBI mode, we end up + * with line 20/21 being YUYV encoded instead of being in 8-bit + * greyscale. The core of the issue is that line 21 (and line 23 for + * PAL WSS) are inside of active video region, and as a result they + * get the pixelformatting associated with that area. So by cropping + * it out, we end up with the same format as the rest of the VBI + * region + */ if (em28xx_vbi_supported(dev) == 1) em28xx_capture_area_set(dev, 0, 2, width, height); else @@ -365,14 +366,16 @@ static int em28xx_set_alternate(struct em28xx *dev) { struct em28xx_v4l2 *v4l2 = dev->v4l2; struct usb_device *udev = interface_to_usbdev(dev->intf); - int errCode; + int err; int i; unsigned int min_pkt_size = v4l2->width * 2 + 4; - /* NOTE: for isoc transfers, only alt settings > 0 are allowed - bulk transfers seem to work only with alt=0 ! */ + /* + * NOTE: for isoc transfers, only alt settings > 0 are allowed + * bulk transfers seem to work only with alt=0 ! + */ dev->alt = 0; - if ((alt > 0) && (alt < dev->num_alt)) { + if (alt > 0 && alt < dev->num_alt) { em28xx_videodbg("alternate forced to %d\n", dev->alt); dev->alt = alt; goto set_alt; @@ -380,9 +383,10 @@ static int em28xx_set_alternate(struct em28xx *dev) if (dev->analog_xfer_bulk) goto set_alt; - /* When image size is bigger than a certain value, - the frame size should be increased, otherwise, only - green screen will be received. + /* + * When image size is bigger than a certain value, + * the frame size should be increased, otherwise, only + * green screen will be received. */ if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2) min_pkt_size *= 2; @@ -392,18 +396,22 @@ static int em28xx_set_alternate(struct em28xx *dev) if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { dev->alt = i; break; - /* otherwise make sure that we end up with the maximum bandwidth - because the min_pkt_size equation might be wrong... - */ + /* + * otherwise make sure that we end up with the maximum + * bandwidth because the min_pkt_size equation might be wrong. + * + */ } else if (dev->alt_max_pkt_size_isoc[i] > dev->alt_max_pkt_size_isoc[dev->alt]) dev->alt = i; } set_alt: - /* NOTE: for bulk transfers, we need to call usb_set_interface() + /* + * NOTE: for bulk transfers, we need to call usb_set_interface() * even if the previous settings were the same. Otherwise streaming - * fails with all urbs having status = -EOVERFLOW ! */ + * fails with all urbs having status = -EOVERFLOW ! + */ if (dev->analog_xfer_bulk) { dev->max_pkt_size = 512; /* USB 2.0 spec */ dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; @@ -416,19 +424,19 @@ set_alt: } em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(udev, dev->ifnum, dev->alt); - if (errCode < 0) { + err = usb_set_interface(udev, dev->ifnum, dev->alt); + if (err < 0) { dev_err(&dev->intf->dev, "cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; + dev->alt, err); + return err; } return 0; } -/* ------------------------------------------------------------------ - DMA and thread functions - ------------------------------------------------------------------*/ +/* + * DMA and thread functions + */ /* * Finish the current buffer @@ -514,8 +522,9 @@ static void em28xx_copy_video(struct em28xx *dev, em28xx_isocdbg("Overflow of %zu bytes past buffer end(2)\n", ((char *)startwrite + lencopy) - ((char *)buf->vb_buf + buf->length)); - lencopy = remain = (char *)buf->vb_buf + buf->length - - (char *)startwrite; + remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; + lencopy = remain; } if (lencopy <= 0) break; @@ -623,11 +632,11 @@ finish_field_prepare_next(struct em28xx *dev, struct em28xx_v4l2 *v4l2 = dev->v4l2; if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */ - if (buf != NULL) + if (buf) finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); } - if (buf != NULL) { + if (buf) { buf->top_field = v4l2->top_field; buf->pos = 0; } @@ -648,13 +657,17 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - /* capture type 0 = vbi start - capture type 1 = vbi in progress - capture type 2 = video start - capture type 3 = video in progress */ + /* + * capture type 0 = vbi start + * capture type 1 = vbi in progress + * capture type 2 = video start + * capture type 3 = video in progress + */ if (data_len >= 4) { - /* NOTE: Headers are always 4 bytes and - * never split across packets */ + /* + * NOTE: Headers are always 4 bytes and + * never split across packets + */ if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { /* Continuation */ @@ -677,8 +690,10 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, data_len -= 4; } } - /* NOTE: With bulk transfers, intermediate data packets - * have no continuation header */ + /* + * NOTE: With bulk transfers, intermediate data packets + * have no continuation header + */ if (v4l2->capture_type == 0) { vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); @@ -692,7 +707,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, (vbi_size - v4l2->vbi_read) : data_len; /* Copy VBI data */ - if (vbi_buf != NULL) + if (vbi_buf) em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); v4l2->vbi_read += vbi_data_len; @@ -710,7 +725,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, v4l2->capture_type = 3; } - if (v4l2->capture_type == 3 && buf != NULL && data_len > 0) + if (v4l2->capture_type == 3 && buf && data_len > 0) em28xx_copy_video(dev, buf, data_pkt, data_len); } @@ -727,8 +742,10 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, bool frame_end = false; /* Check for header */ - /* NOTE: at least with bulk transfers, only the first packet - * has a header and has always set the FRAME_END bit */ + /* + * NOTE: at least with bulk transfers, only the first packet + * has a header and has always set the FRAME_END bit + */ if (data_len >= 2) { /* em25xx header is only 2 bytes long */ if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) && ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) { @@ -745,14 +762,15 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, buf = finish_field_prepare_next(dev, buf, dmaq); dev->usb_ctl.vid_buf = buf; } - /* NOTE: in ISOC mode when a new frame starts and buf==NULL, + /* + * NOTE: in ISOC mode when a new frame starts and buf==NULL, * we COULD already prepare a buffer here to avoid skipping the * first frame. */ } /* Copy data */ - if (buf != NULL && data_len > 0) + if (buf && data_len > 0) em28xx_copy_video(dev, buf, data_pkt, data_len); /* Finish frame (ISOC only) => avoids lag of 1 frame */ @@ -761,14 +779,17 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, dev->usb_ctl.vid_buf = buf; } - /* NOTE: Tested with USB bulk transfers only ! + /* + * NOTES: + * + * 1) Tested with USB bulk transfers only ! * The wording in the datasheet suggests that isoc might work different. * The current code assumes that with isoc transfers each packet has a * header like with the other em28xx devices. + * + * 2) Support for interlaced mode is pure theory. It has not been + * tested and it is unknown if these devices actually support it. */ - /* NOTE: Support for interlaced mode is pure theory. It has not been - * tested and it is unknown if these devices actually support it. */ - /* NOTE: No VBI support yet (these chips likely do not support VBI). */ } /* Processes and copies the URB data content (video and VBI data) */ @@ -829,12 +850,11 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) else process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); - } return 1; } -static int get_ressource(enum v4l2_buf_type f_type) +static int get_resource(enum v4l2_buf_type f_type) { switch (f_type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -842,14 +862,15 @@ static int get_ressource(enum v4l2_buf_type f_type) case V4L2_BUF_TYPE_VBI_CAPTURE: return EM28XX_RESOURCE_VBI; default: - BUG(); + WARN_ON(1); + return -1; /* Indicate that device is busy */ } } /* Usage lock check functions */ static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) { - int res_type = get_ressource(f_type); + int res_type = get_resource(f_type); /* is it free? */ if (dev->resources & res_type) { @@ -865,7 +886,7 @@ static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) { - int res_type = get_ressource(f_type); + int res_type = get_resource(f_type); dev->resources &= ~res_type; em28xx_videodbg("res: put %d\n", res_type); @@ -937,10 +958,11 @@ static int em28xx_enable_analog_tuner(struct em28xx *dev) flags ? "enabled" : "disabled", ret); return ret; - } else - em28xx_videodbg("link %s->%s was %s\n", - source->name, sink->name, - flags ? "ENABLED" : "disabled"); + } + + em28xx_videodbg("link %s->%s was %s\n", + source->name, sink->name, + flags ? "ENABLED" : "disabled"); } #endif return 0; @@ -976,7 +998,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) } /* Webcams don't have input connectors */ - if (dev->board.is_webcam) + if (dev->is_webcam) return; /* Create entities for each input connector */ @@ -1016,10 +1038,9 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) #endif } - -/* ------------------------------------------------------------------ - Videobuf2 operations - ------------------------------------------------------------------*/ +/* + * Videobuf2 operations + */ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -1072,8 +1093,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) em28xx_videodbg("%s\n", __func__); - /* Make sure streaming is not already in progress for this type - of filehandle (e.g. video, vbi) */ + /* + * Make sure streaming is not already in progress for this type + * of filehandle (e.g. video, vbi) + */ rc = res_get(dev, vq->type); if (rc) return rc; @@ -1084,9 +1107,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) /* Allocate the USB bandwidth */ em28xx_set_alternate(dev); - /* Needed, since GPIO might have disabled power of - some i2c device - */ + /* + * Needed, since GPIO might have disabled power of + * some i2c device + */ em28xx_wake_i2c(dev); v4l2->capture_type = -1; @@ -1145,7 +1169,7 @@ static void em28xx_stop_streaming(struct vb2_queue *vq) } spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vid_buf != NULL) { + if (dev->usb_ctl.vid_buf) { vb2_buffer_done(&dev->usb_ctl.vid_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dev->usb_ctl.vid_buf = NULL; @@ -1180,7 +1204,7 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq) } spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vbi_buf != NULL) { + if (dev->usb_ctl.vbi_buf) { vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dev->usb_ctl.vbi_buf = NULL; @@ -1261,7 +1285,9 @@ static int em28xx_vb2_setup(struct em28xx *dev) return 0; } -/********************* v4l2 interface **************************************/ +/* + * v4l2 interface + */ static void video_mux(struct em28xx *dev, int index) { @@ -1277,7 +1303,7 @@ static void video_mux(struct em28xx *dev, int index) v4l2_device_call_all(v4l2_dev, 0, video, s_routing, INPUT(index)->vmux, 0, 0); - if (dev->board.has_msp34xx) { + if (dev->has_msp34xx) { if (dev->i2s_speed) { v4l2_device_call_all(v4l2_dev, 0, audio, s_i2s_clock_freq, dev->i2s_speed); @@ -1394,9 +1420,9 @@ static void scale_to_size(struct em28xx *dev, *height = 1; } -/* ------------------------------------------------------------------ - IOCTL vidioc handling - ------------------------------------------------------------------*/ +/* + * IOCTL vidioc handling + */ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) @@ -1462,8 +1488,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, if (width == maxw && height == maxh) width /= 2; } else { - /* width must even because of the YUYV format - height must be even because of interlacing */ + /* + * width must even because of the YUYV format + * height must be even because of interlacing + */ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); } @@ -1493,7 +1521,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, - unsigned width, unsigned height) + unsigned int width, unsigned int height) { struct em28xx_fmt *fmt; struct em28xx_v4l2 *v4l2 = dev->v4l2; @@ -1582,17 +1610,26 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { + struct v4l2_subdev_frame_interval ival = { 0 }; struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; int rc = 0; + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; - if (dev->board.is_webcam) + p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + if (dev->is_webcam) { rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, - video, g_parm, p); - else + video, g_frame_interval, &ival); + if (!rc) + p->parm.capture.timeperframe = ival.interval; + } else { v4l2_video_std_frame_period(v4l2->norm, &p->parm.capture.timeperframe); + } return rc; } @@ -1601,10 +1638,27 @@ static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { struct em28xx *dev = video_drvdata(file); + struct v4l2_subdev_frame_interval ival = { + 0, + p->parm.capture.timeperframe + }; + int rc = 0; + + if (!dev->is_webcam) + return -ENOTTY; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + memset(&p->parm, 0, sizeof(p->parm)); p->parm.capture.readbuffers = EM28XX_MIN_BUF; - return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, - 0, video, s_parm, p); + p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0, + video, s_frame_interval, &ival); + if (!rc) + p->parm.capture.timeperframe = ival.interval; + return rc; } static int vidioc_enum_input(struct file *file, void *priv, @@ -1616,20 +1670,19 @@ static int vidioc_enum_input(struct file *file, void *priv, n = i->index; if (n >= MAX_EM28XX_INPUT) return -EINVAL; - if (0 == INPUT(n)->type) + if (!INPUT(n)->type) return -EINVAL; - i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name, iname[INPUT(n)->type]); - if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type)) + if (INPUT(n)->type == EM28XX_VMUX_TELEVISION) i->type = V4L2_INPUT_TYPE_TUNER; i->std = dev->v4l2->vdev.tvnorms; /* webcams do not have the STD API */ - if (dev->board.is_webcam) + if (dev->is_webcam) i->capabilities = 0; return 0; @@ -1650,7 +1703,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) if (i >= MAX_EM28XX_INPUT) return -EINVAL; - if (0 == INPUT(i)->type) + if (!INPUT(i)->type) return -EINVAL; video_mux(dev, i); @@ -1696,13 +1749,14 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, + const struct v4l2_audio *a) { struct em28xx *dev = video_drvdata(file); if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; - if (0 == INPUT(a->index)->type) + if (!INPUT(a->index)->type) return -EINVAL; dev->ctl_ainput = INPUT(a->index)->amux; @@ -1719,7 +1773,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct em28xx *dev = video_drvdata(file); - if (0 != t->index) + if (t->index != 0) return -EINVAL; strcpy(t->name, "Tuner"); @@ -1733,7 +1787,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct em28xx *dev = video_drvdata(file); - if (0 != t->index) + if (t->index != 0) return -EINVAL; v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); @@ -1746,7 +1800,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (0 != f->tuner) + if (f->tuner != 0) return -EINVAL; f->frequency = v4l2->frequency; @@ -1760,7 +1814,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (0 != f->tuner) + if (f->tuner != 0) return -EINVAL; v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); @@ -1884,8 +1938,9 @@ static int vidioc_querycap(struct file *file, void *priv, if (dev->tuner_type != TUNER_ABSENT) cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | + V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (video_is_registered(&v4l2->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (video_is_registered(&v4l2->radio_dev)) @@ -1978,9 +2033,9 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, return 0; } -/* ----------------------------------------------------------- */ -/* RADIO ESPECIFIC IOCTLS */ -/* ----------------------------------------------------------- */ +/* + * RADIO ESPECIFIC IOCTLS + */ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) @@ -2002,7 +2057,7 @@ static int radio_s_tuner(struct file *file, void *priv, { struct em28xx *dev = video_drvdata(file); - if (0 != t->index) + if (t->index != 0) return -EINVAL; v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); @@ -2097,7 +2152,7 @@ static int em28xx_v4l2_open(struct file *filp) * em28xx_v4l2_fini() * unregisters the v4l2,i2c and usb devices * called when the device gets disconected or at module unload -*/ + */ static int em28xx_v4l2_fini(struct em28xx *dev) { struct em28xx_v4l2 *v4l2 = dev->v4l2; @@ -2112,7 +2167,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) return 0; } - if (v4l2 == NULL) + if (!v4l2) return 0; dev_info(&dev->intf->dev, "Closing video extension\n"); @@ -2127,17 +2182,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (video_is_registered(&v4l2->radio_dev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", - video_device_node_name(&v4l2->radio_dev)); + video_device_node_name(&v4l2->radio_dev)); video_unregister_device(&v4l2->radio_dev); } if (video_is_registered(&v4l2->vbi_dev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", - video_device_node_name(&v4l2->vbi_dev)); + video_device_node_name(&v4l2->vbi_dev)); video_unregister_device(&v4l2->vbi_dev); } if (video_is_registered(&v4l2->vdev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", - video_device_node_name(&v4l2->vdev)); + video_device_node_name(&v4l2->vdev)); video_unregister_device(&v4l2->vdev); } @@ -2189,7 +2244,7 @@ static int em28xx_v4l2_close(struct file *filp) struct em28xx *dev = video_drvdata(filp); struct em28xx_v4l2 *v4l2 = dev->v4l2; struct usb_device *udev = interface_to_usbdev(dev->intf); - int errCode; + int err; em28xx_videodbg("users=%d\n", v4l2->users); @@ -2202,7 +2257,7 @@ static int em28xx_v4l2_close(struct file *filp) goto exit; /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); /* do this before setting alternate! */ em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -2210,11 +2265,11 @@ static int em28xx_v4l2_close(struct file *filp) /* set alternate 0 */ dev->alt = 0; em28xx_videodbg("setting alternate 0\n"); - errCode = usb_set_interface(udev, 0, 0); - if (errCode < 0) { + err = usb_set_interface(udev, 0, 0); + if (err < 0) { dev_err(&dev->intf->dev, "cannot change alternate number to 0 (error=%i)\n", - errCode); + err); } } @@ -2343,7 +2398,7 @@ static void em28xx_vdev_init(struct em28xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2->v4l2_dev; vfd->lock = &dev->lock; - if (dev->board.is_webcam) + if (dev->is_webcam) vfd->tvnorms = 0; snprintf(vfd->name, sizeof(vfd->name), "%s %s", @@ -2372,7 +2427,7 @@ static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr) 0, tuner, s_type_addr, &tun_setup); } - if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { + if (dev->tuner_type != TUNER_ABSENT && dev->tuner_type) { tun_setup.type = dev->tuner_type; tun_setup.addr = tuner_addr; @@ -2435,7 +2490,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) mutex_lock(&dev->lock); - v4l2 = kzalloc(sizeof(struct em28xx_v4l2), GFP_KERNEL); + v4l2 = kzalloc(sizeof(*v4l2), GFP_KERNEL); if (!v4l2) { mutex_unlock(&dev->lock); return -ENOMEM; @@ -2458,7 +2513,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2_ctrl_handler_init(hdl, 8); v4l2->v4l2_dev.ctrl_handler = hdl; - if (dev->board.is_webcam) + if (dev->is_webcam) v4l2->progressive = true; /* @@ -2470,7 +2525,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* request some modules */ - if (dev->board.has_msp34xx) + if (dev->has_msp34xx) v4l2_i2c_new_subdev(&v4l2->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "msp3400", 0, msp3400_addrs); @@ -2559,7 +2614,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vbiq.active); - if (dev->board.has_msp34xx) { + if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); if (ret < 0) { @@ -2568,7 +2623,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) __func__, ret); goto unregister_dev; } - msleep(3); + usleep_range(10000, 11000); ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); if (ret < 0) { @@ -2577,7 +2632,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) __func__, ret); goto unregister_dev; } - msleep(3); + usleep_range(10000, 11000); } /* set default norm */ @@ -2589,8 +2644,10 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->format = &format[0]; maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ + /* + * MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% + */ if (dev->board.is_em2800) maxw /= 2; @@ -2611,29 +2668,33 @@ static int em28xx_v4l2_init(struct em28xx *dev) em28xx_set_outfmt(dev); /* Add image controls */ - /* NOTE: at this point, the subdevices are already registered, so bridge - * controls are only added/enabled when no subdevice provides them */ - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST)) + + /* + * NOTE: at this point, the subdevices are already registered, so + * bridge controls are only added/enabled when no subdevice provides + * them + */ + if (!v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_CONTRAST, 0, 0x1f, 1, CONTRAST_DEFAULT); - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS)) + if (!v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_BRIGHTNESS, -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT); - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SATURATION)) + if (!v4l2_ctrl_find(hdl, V4L2_CID_SATURATION)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_SATURATION, 0, 0x1f, 1, SATURATION_DEFAULT); - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE)) + if (!v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT); - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE)) + if (!v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, RED_BALANCE_DEFAULT); - if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS)) + if (!v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS)) v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_SHARPNESS, 0, 0x0f, 1, SHARPNESS_DEFAULT); @@ -2653,7 +2714,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vdev.queue->lock = &v4l2->vb_queue_lock; /* disable inapplicable ioctls */ - if (dev->board.is_webcam) { + if (dev->is_webcam) { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD); @@ -2683,7 +2744,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* Allocate and fill vbi video_device struct */ if (em28xx_vbi_supported(dev) == 1) { em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template, - "vbi"); + "vbi"); v4l2->vbi_dev.queue = &v4l2->vb_vbiq; v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; @@ -2713,7 +2774,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template, - "radio"); + "radio"); ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { @@ -2749,7 +2810,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) video_device_node_name(&v4l2->vbi_dev)); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); /* initialize videobuf2 stuff */ em28xx_vb2_setup(dev); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 88084f24f033..63c7c6124707 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -1,31 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* - em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com> - Ludovico Cavedon <cavedon@sssup.it> - Mauro Carvalho Chehab <mchehab@infradead.org> - Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> - - Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices + * + * Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com> + * Ludovico Cavedon <cavedon@sssup.it> + * Mauro Carvalho Chehab <mchehab@infradead.org> + * Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> + * + * Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de> + * + * 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 _EM28XX_H #define _EM28XX_H +#include <linux/bitfield.h> + #define EM28XX_VERSION "0.2.2" #define DRIVER_DESC "Empia em28xx device driver" @@ -166,7 +165,7 @@ #define EM28XX_STOP_AUDIO 0 /* maximum number of em28xx boards */ -#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ +#define EM28XX_MAXBOARDS DVB_MAX_ADAPTERS /* All adapters could be em28xx */ /* maximum number of frames that can be queued */ #define EM28XX_NUM_FRAMES 5 @@ -180,43 +179,32 @@ /* max number of I2C buses on em28xx devices */ #define NUM_I2C_BUSES 2 -/* isoc transfers: number of packets for each buffer - windows requests only 64 packets .. so we better do the same - this is what I found out for all alternate numbers there! +/* + * isoc transfers: number of packets for each buffer + * windows requests only 64 packets .. so we better do the same + * this is what I found out for all alternate numbers there! */ #define EM28XX_NUM_ISOC_PACKETS 64 #define EM28XX_DVB_NUM_ISOC_PACKETS 64 -/* bulk transfers: transfer buffer size = packet size * packet multiplier - USB 2.0 spec says bulk packet size is always 512 bytes +/* + * bulk transfers: transfer buffer size = packet size * packet multiplier + * USB 2.0 spec says bulk packet size is always 512 bytes */ #define EM28XX_BULK_PACKET_MULTIPLIER 384 -#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 94 #define EM28XX_INTERLACED_DEFAULT 1 -/* - * Time in msecs to wait for i2c xfers to finish. - * 35ms is the maximum time a SMBUS device could wait when - * clock stretching is used. As the transfer itself will take - * some time to happen, set it to 35 ms. - * - * Ok, I2C doesn't specify any limit. So, eventually, we may need - * to increase this timeout. - * - * FIXME: this assumes that an I2C message is not longer than 1ms. - * This is actually dependent on the I2C bus speed, although most - * devices use a 100kHz clock. So, this assumtion is true most of - * the time. - */ -#define EM28XX_I2C_XFER_TIMEOUT 36 - /* time in msecs to wait for AC97 xfers to finish */ #define EM28XX_AC97_XFER_TIMEOUT 100 /* max. number of button state polling addresses */ #define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5 +#define PRIMARY_TS 0 +#define SECONDARY_TS 1 + enum em28xx_mode { EM28XX_SUSPEND, EM28XX_ANALOG_MODE, @@ -225,64 +213,83 @@ enum em28xx_mode { struct em28xx; +/** + * struct em28xx_usb_bufs - Contains URB-related buffer data + * + * @max_pkt_size: max packet size of isoc transaction + * @num_packets: number of packets in each buffer + * @num_bufs: number of allocated urb + * @urb: urb for isoc/bulk transfers + * @buf: transfer buffers for isoc/bulk transfer + */ struct em28xx_usb_bufs { - /* max packet size of isoc transaction */ int max_pkt_size; - - /* number of packets in each buffer */ int num_packets; - - /* number of allocated urbs */ int num_bufs; - - /* urb for isoc/bulk transfers */ struct urb **urb; - - /* transfer buffers for isoc/bulk transfer */ - char **transfer_buffer; + char **buf; }; +/** + * struct em28xx_usb_ctl - Contains URB-related buffer data + * + * @analog_bufs: isoc/bulk transfer buffers for analog mode + * @digital_bufs: isoc/bulk transfer buffers for digital mode + * @vid_buf: Stores already requested video buffers + * @vbi_buf: Stores already requested VBI buffers + * @urb_data_copy: copy data from URB + */ struct em28xx_usb_ctl { - /* isoc/bulk transfer buffers for analog mode */ struct em28xx_usb_bufs analog_bufs; - - /* isoc/bulk transfer buffers for digital mode */ struct em28xx_usb_bufs digital_bufs; - - /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - - /* copy data from URB */ int (*urb_data_copy)(struct em28xx *dev, struct urb *urb); - }; -/* Struct to enumberate video formats */ +/** + * struct em28xx_fmt - Struct to enumberate video formats + * + * @name: Name for the video standard + * @fourcc: v4l2 format id + * @depth: mean number of bits to represent a pixel + * @reg: em28xx register value to set it + */ struct em28xx_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; - int reg; + char *name; + u32 fourcc; + int depth; + int reg; }; -/* buffer for one video frame */ +/** + * struct em28xx_buffer- buffer for storing one video frame + * + * @vb: common v4l buffer stuff + * @list: List to associate it with the other buffers + * @mem: pointer to the buffer, as returned by vb2_plane_vaddr() + * @length: length of the buffer, as returned by vb2_plane_size() + * @top_field: If non-zero, indicate that the buffer is the top field + * @pos: Indicate the next position of the buffer to be filled. + * @vb_buf: pointer to vmalloc memory address in vb + * + * .. note:: + * + * in interlaced mode, @pos is reset to zero at the start of each new + * field (not frame !) + */ struct em28xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; + struct vb2_v4l2_buffer vb; /* must be first */ - void *mem; - unsigned int length; - int top_field; + struct list_head list; - /* counter to control buffer fill */ - unsigned int pos; - /* NOTE; in interlaced mode, this value is reset to zero at - * the start of each new field (not frame !) */ + void *mem; + unsigned int length; + int top_field; - /* pointer to vmalloc memory address in vb */ - char *vb_buf; + unsigned int pos; + + char *vb_buf; }; struct em28xx_dmaqueue { @@ -324,20 +331,48 @@ enum em28xx_usb_audio_type { EM28XX_USB_AUDIO_VENDOR, }; -/* em28xx has two audio inputs: tuner and line in. - However, on most devices, an auxiliary AC97 codec device is used. - The AC97 device may have several different inputs and outputs, - depending on their model. So, it is possible to use AC97 mixer to - address more than two different entries. +/** + * em28xx_amux - describes the type of audio input used by em28xx + * + * @EM28XX_AMUX_VIDEO: + * On devices without AC97, this is the only value that it is currently + * allowed. + * On devices with AC97, it corresponds to the AC97 mixer "Video" control. + * @EM28XX_AMUX_LINE_IN: + * Only for devices with AC97. Corresponds to AC97 mixer "Line In". + * @EM28XX_AMUX_VIDEO2: + * Only for devices with AC97. It means that em28xx should use "Line In" + * And AC97 should use the "Video" mixer control. + * @EM28XX_AMUX_PHONE: + * Only for devices with AC97. Corresponds to AC97 mixer "Phone". + * @EM28XX_AMUX_MIC: + * Only for devices with AC97. Corresponds to AC97 mixer "Mic". + * @EM28XX_AMUX_CD: + * Only for devices with AC97. Corresponds to AC97 mixer "CD". + * @EM28XX_AMUX_AUX: + * Only for devices with AC97. Corresponds to AC97 mixer "Aux". + * @EM28XX_AMUX_PCM_OUT: + * Only for devices with AC97. Corresponds to AC97 mixer "PCM out". + * + * The em28xx chip itself has only two audio inputs: tuner and line in. + * On almost all devices, only the tuner input is used. + * + * However, on most devices, an auxiliary AC97 codec device is used, + * usually connected to the em28xx tuner input (except for + * @EM28XX_AMUX_LINE_IN). + * + * The AC97 device typically have several different inputs and outputs. + * The exact number and description depends on their model. + * + * It is possible to AC97 to mixer more than one different entries at the + * same time, via the alsa mux. */ enum em28xx_amux { - /* This is the only entry for em28xx tuner input */ - EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */ - - EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */ + EM28XX_AMUX_VIDEO, + EM28XX_AMUX_LINE_IN, /* Some less-common mixer setups */ - EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */ + EM28XX_AMUX_VIDEO2, EM28XX_AMUX_PHONE, EM28XX_AMUX_MIC, EM28XX_AMUX_CD, @@ -347,14 +382,14 @@ enum em28xx_amux { enum em28xx_aout { /* AC97 outputs */ - EM28XX_AOUT_MASTER = 1 << 0, - EM28XX_AOUT_LINE = 1 << 1, - EM28XX_AOUT_MONO = 1 << 2, - EM28XX_AOUT_LFE = 1 << 3, - EM28XX_AOUT_SURR = 1 << 4, + EM28XX_AOUT_MASTER = BIT(0), + EM28XX_AOUT_LINE = BIT(1), + EM28XX_AOUT_MONO = BIT(2), + EM28XX_AOUT_LFE = BIT(3), + EM28XX_AOUT_SURR = BIT(4), /* PCM IN Mixer - used by AC97_RECORD_SELECT register */ - EM28XX_AOUT_PCM_IN = 1 << 7, + EM28XX_AOUT_PCM_IN = BIT(7), /* Bits 10-8 are used to indicate the PCM IN record select */ EM28XX_AOUT_PCM_MIC_PCM = 0 << 8, @@ -383,7 +418,7 @@ struct em28xx_input { unsigned int vmux; enum em28xx_amux amux; enum em28xx_aout aout; - struct em28xx_reg_seq *gpio; + const struct em28xx_reg_seq *gpio; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) @@ -441,22 +476,23 @@ struct em28xx_board { int vchannels; int tuner_type; int tuner_addr; - unsigned def_i2c_bus; /* Default I2C bus */ + unsigned int def_i2c_bus; /* Default I2C bus */ /* i2c flags */ unsigned int tda9887_conf; /* GPIO sequences */ - struct em28xx_reg_seq *dvb_gpio; - struct em28xx_reg_seq *suspend_gpio; - struct em28xx_reg_seq *tuner_gpio; - struct em28xx_reg_seq *mute_gpio; + const struct em28xx_reg_seq *dvb_gpio; + const struct em28xx_reg_seq *suspend_gpio; + const struct em28xx_reg_seq *tuner_gpio; + const struct em28xx_reg_seq *mute_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; + unsigned int has_dual_ts:1; unsigned int is_webcam:1; unsigned int valid:1; unsigned int has_ir_i2c:1; @@ -476,7 +512,7 @@ struct em28xx_board { struct em28xx_led *leds; /* Buttons */ - struct em28xx_button *buttons; + const struct em28xx_button *buttons; }; struct em28xx_eeprom { @@ -519,8 +555,8 @@ struct em28xx_v4l2 { /* Videobuf2 */ struct vb2_queue vb_vidq; struct vb2_queue vb_vbiq; - struct mutex vb_queue_lock; - struct mutex vb_vbi_queue_lock; + struct mutex vb_queue_lock; /* Protects vb_vidq */ + struct mutex vb_vbi_queue_lock; /* Protects vb_vbiq */ u8 vinmode; u8 vinctl; @@ -546,8 +582,8 @@ struct em28xx_v4l2 { /* Frame properties */ int width; /* current frame width */ int height; /* current frame height */ - unsigned hscale; /* horizontal scale factor (see datasheet) */ - unsigned vscale; /* vertical scale factor (see datasheet) */ + unsigned int hscale; /* horizontal scale factor (see datasheet) */ + unsigned int vscale; /* vertical scale factor (see datasheet) */ unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ @@ -565,7 +601,7 @@ struct em28xx_v4l2 { struct em28xx_audio { char name[50]; - unsigned num_urb; + unsigned int num_urb; char **transfer_buffer; struct urb **urb; struct usb_device *udev; @@ -578,7 +614,7 @@ struct em28xx_audio { size_t period; int users; - spinlock_t slock; + spinlock_t slock; /* Protects struct em28xx_audio */ /* Controls streaming */ struct work_struct wq_trigger; /* trigger to start/stop audio */ @@ -596,7 +632,7 @@ enum em28xx_i2c_algo_type { struct em28xx_i2c_bus { struct em28xx *dev; - unsigned bus; + unsigned int bus; enum em28xx_i2c_algo_type algo_type; }; @@ -604,102 +640,111 @@ struct em28xx_i2c_bus { struct em28xx { struct kref ref; - /* Sub-module data */ + // Sub-module data struct em28xx_v4l2 *v4l2; struct em28xx_dvb *dvb; struct em28xx_audio adev; struct em28xx_IR *ir; - /* generic device properties */ - int model; /* index in the device_data struct */ - int devno; /* marks the number of this device */ + // generic device properties + int model; // index in the device_data struct + int devno; // marks the number of this device enum em28xx_chip_id chip_id; - unsigned int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */ - unsigned char disconnected:1; /* device has been diconnected */ + unsigned int is_em25xx:1; // em25xx/em276x/7x/8x family bridge + unsigned int disconnected:1; // device has been diconnected unsigned int has_video:1; unsigned int is_audio_only:1; + unsigned int is_webcam:1; + unsigned int has_msp34xx:1; + unsigned int i2c_speed:2; enum em28xx_int_audio_type int_audio_type; enum em28xx_usb_audio_type usb_audio_type; + unsigned char name[32]; struct em28xx_board board; - enum em28xx_sensor em28xx_sensor; /* camera specific */ + enum em28xx_sensor em28xx_sensor; // camera specific - /* Some older em28xx chips needs a waiting time after writing */ + // Some older em28xx chips needs a waiting time after writing unsigned int wait_after_write; struct list_head devlist; - u32 i2s_speed; /* I2S speed for audio digital stream */ + u32 i2s_speed; // I2S speed for audio digital stream struct em28xx_audio_mode audio_mode; - int tuner_type; /* type of the tuner */ + int tuner_type; // type of the tuner - /* i2c i/o */ + // i2c i/o struct i2c_adapter i2c_adap[NUM_I2C_BUSES]; struct i2c_client i2c_client[NUM_I2C_BUSES]; struct em28xx_i2c_bus i2c_bus[NUM_I2C_BUSES]; unsigned char eeprom_addrwidth_16bit:1; - unsigned def_i2c_bus; /* Default I2C bus */ - unsigned cur_i2c_bus; /* Current I2C bus */ + unsigned int def_i2c_bus; // Default I2C bus + unsigned int cur_i2c_bus; // Current I2C bus struct rt_mutex i2c_bus_lock; - /* video for linux */ - unsigned int ctl_input; /* selected input */ - unsigned int ctl_ainput;/* selected audio input */ - unsigned int ctl_aoutput;/* selected audio output */ + // video for linux + unsigned int ctl_input; // selected input + unsigned int ctl_ainput;// selected audio input + unsigned int ctl_aoutput;// selected audio output int mute; int volume; - unsigned long hash; /* eeprom hash - for boards with generic ID */ - unsigned long i2c_hash; /* i2c devicelist hash - - for boards with generic ID */ + unsigned long hash; // eeprom hash - for boards with generic ID + unsigned long i2c_hash; // i2c devicelist hash - + // for boards with generic ID struct work_struct request_module_wk; - /* locks */ - struct mutex lock; + // locks + struct mutex lock; /* protects em28xx struct */ struct mutex ctrl_urb_lock; /* protects urb_buf */ - /* resources in use */ + // resources in use unsigned int resources; - /* eeprom content */ + // eeprom content u8 *eedata; u16 eedata_len; - /* Isoc control struct */ + // Isoc control struct struct em28xx_dmaqueue vidq; struct em28xx_dmaqueue vbiq; struct em28xx_usb_ctl usb_ctl; - spinlock_t slock; - - /* usb transfer */ - struct usb_interface *intf; /* the usb interface */ - u8 ifnum; /* number of the assigned usb interface */ - u8 analog_ep_isoc; /* address of isoc endpoint for analog */ - u8 analog_ep_bulk; /* address of bulk endpoint for analog */ - u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ - u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */ - int alt; /* alternate setting */ - int max_pkt_size; /* max packet size of the selected ep at alt */ - int packet_multiplier; /* multiplier for wMaxPacketSize, used for - URB buffer size definition */ - int num_alt; /* number of alternative settings */ - unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ - unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc - transfers for analog */ - int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ - unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the - selected DVB ep at dvb_alt */ - unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc - transfers for DVB */ - char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ - - /* helper funcs that call usb_control_msg */ + + spinlock_t slock; /* Protects em28xx video/vbi/dvb IRQ stream data */ + + // usb transfer + struct usb_interface *intf; // the usb interface + u8 ifnum; // number of the assigned usb interface + u8 analog_ep_isoc; // address of isoc endpoint for analog + u8 analog_ep_bulk; // address of bulk endpoint for analog + u8 dvb_ep_isoc_ts2; // address of isoc endpoint for DVB TS2 + u8 dvb_ep_bulk_ts2; // address of bulk endpoint for DVB TS2 + u8 dvb_ep_isoc; // address of isoc endpoint for DVB + u8 dvb_ep_bulk; // address of bulk endpoint for DVB + int alt; // alternate setting + int max_pkt_size; // max packet size of the selected ep at alt + int packet_multiplier; // multiplier for wMaxPacketSize, used for + // URB buffer size definition + int num_alt; // number of alternative settings + unsigned int *alt_max_pkt_size_isoc; // array of isoc wMaxPacketSize + unsigned int analog_xfer_bulk:1; // use bulk instead of isoc + // transfers for analog + int dvb_alt_isoc; // alternate setting for DVB isoc transfers + unsigned int dvb_max_pkt_size_isoc; // isoc max packet size of the + // selected DVB ep at dvb_alt + unsigned int dvb_max_pkt_size_isoc_ts2; // isoc max packet size of the + // selected DVB ep at dvb_alt + unsigned int dvb_xfer_bulk:1; // use bulk instead of isoc + // transfers for DVB + char urb_buf[URB_MAX_CTRL_SIZE]; // urb control msg buffer + + // helper funcs that call usb_control_msg int (*em28xx_write_regs)(struct em28xx *dev, u16 reg, char *buf, int len); int (*em28xx_read_reg)(struct em28xx *dev, u16 reg); @@ -711,14 +756,14 @@ struct em28xx { enum em28xx_mode mode; - /* Button state polling */ + // Button state polling struct delayed_work buttons_query_work; u8 button_polling_addresses[EM28XX_NUM_BUTTON_ADDRESSES_MAX]; u8 button_polling_last_values[EM28XX_NUM_BUTTON_ADDRESSES_MAX]; u8 num_button_polling_addresses; - u16 button_polling_interval; /* [ms] */ - /* Snapshot button input device */ - char snapshot_button_path[30]; /* path of the input dev */ + u16 button_polling_interval; // [ms] + // Snapshot button input device + char snapshot_button_path[30]; // path of the input dev struct input_dev *sbutton_input_dev; #ifdef CONFIG_MEDIA_CONTROLLER @@ -726,6 +771,9 @@ struct em28xx { struct media_entity input_ent[MAX_EM28XX_INPUT]; struct media_pad input_pad[MAX_EM28XX_INPUT]; #endif + + struct em28xx *dev_next; + int ts; }; #define kref_to_dev(d) container_of(d, struct em28xx, ref) @@ -734,17 +782,17 @@ struct em28xx_ops { struct list_head next; char *name; int id; - int (*init)(struct em28xx *); - int (*fini)(struct em28xx *); - int (*suspend)(struct em28xx *); - int (*resume)(struct em28xx *); + int (*init)(struct em28xx *dev); + int (*fini)(struct em28xx *dev); + int (*suspend)(struct em28xx *dev); + int (*resume)(struct em28xx *dev); }; /* Provided by em28xx-i2c.c */ -void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus); -int em28xx_i2c_register(struct em28xx *dev, unsigned bus, +void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus); +int em28xx_i2c_register(struct em28xx *dev, unsigned int bus, enum em28xx_i2c_algo_type algo_type); -int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus); +int em28xx_i2c_unregister(struct em28xx *dev, unsigned int bus); /* Provided by em28xx-core.c */ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, @@ -778,7 +826,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); -int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); +int em28xx_gpio_set(struct em28xx *dev, const struct em28xx_reg_seq *gpio); int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); void em28xx_init_extension(struct em28xx *dev); @@ -787,7 +835,7 @@ int em28xx_suspend_extension(struct em28xx *dev); int em28xx_resume_extension(struct em28xx *dev); /* Provided by em28xx-cards.c */ -extern struct em28xx_board em28xx_boards[]; +extern const struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl); diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index c618764480c6..f84a2130f033 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -227,7 +227,7 @@ int go7007_snd_init(struct go7007 *go) { static int dev; struct go7007_snd *gosnd; - int ret = 0; + int ret; if (dev >= SNDRV_CARDS) return -ENODEV; diff --git a/drivers/media/usb/gspca/dtcs033.c b/drivers/media/usb/gspca/dtcs033.c index cdf27cf0112a..7654c8c08eda 100644 --- a/drivers/media/usb/gspca/dtcs033.c +++ b/drivers/media/usb/gspca/dtcs033.c @@ -76,12 +76,10 @@ static int reg_reqs(struct gspca_dev *gspca_dev, } else if (preq->bRequestType & USB_DIR_IN) { gspca_dbg(gspca_dev, D_STREAM, - "USB IN (%d) returned[%d] %02X %02X %02X %s\n", + "USB IN (%d) returned[%d] %3ph %s", i, preq->wLength, - gspca_dev->usb_buf[0], - gspca_dev->usb_buf[1], - gspca_dev->usb_buf[2], + gspca_dev->usb_buf, preq->wLength > 3 ? "...\n" : "\n"); } diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 8c2a86d71e8a..82927eb334c4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -648,8 +648,8 @@ static void s2255_fillbuff(struct s2255_vc *vc, pr_err("s2255: =======no frame\n"); return; } - dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n", - (unsigned long)vbuf, pos); + dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n", + vbuf, pos); } @@ -803,10 +803,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; else f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; } else { @@ -820,10 +816,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) f->fmt.pix.width = LINE_SZ_4CIFS_PAL; - else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) - f->fmt.pix.width = LINE_SZ_2CIFS_PAL; - else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) - f->fmt.pix.width = LINE_SZ_1CIFS_PAL; else f->fmt.pix.width = LINE_SZ_1CIFS_PAL; } diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index f13e4b01b5a5..6d436e9e454f 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -179,8 +179,7 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev, smsusb_onresponse, surb ); - surb->urb.transfer_dma = surb->cb->phys; - surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + surb->urb.transfer_flags |= URB_FREE_BUFFER; return usb_submit_urb(&surb->urb, GFP_ATOMIC); } @@ -446,6 +445,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) dev->in_ep, dev->out_ep); params.device = &dev->udev->dev; + params.usb_device = dev->udev; params.buffer_size = dev->buffer_size; params.num_buffers = MAX_BUFFERS; params.sendrequest_handler = smsusb_sendrequest; diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c index 4d5f4cc4887e..70939e96b856 100644 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -1174,7 +1174,7 @@ static int tm6000_usb_probe(struct usb_interface *interface, { struct usb_device *usbdev; struct tm6000_core *dev; - int i, rc = 0; + int i, rc; int nr = 0; char *speed; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 8314d3fa9241..b2399d4266da 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1346,9 +1346,8 @@ static int __tm6000_open(struct file *file) fh->width = dev->width; fh->height = dev->height; - dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", - (unsigned long)fh, (unsigned long)dev, - (unsigned long)&dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n", + fh, dev, &dev->vidq); dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n", list_empty(&dev->vidq.queued)); dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n", diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index a8900f5571f7..44ca66cb9b8f 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -428,7 +428,7 @@ static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data) struct ttusb_dec *dec = priv; dec->audio_filter->feed->cb.ts(data, 188, NULL, 0, - &dec->audio_filter->feed->feed.ts); + &dec->audio_filter->feed->feed.ts, NULL); return 0; } @@ -438,7 +438,7 @@ static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data) struct ttusb_dec *dec = priv; dec->video_filter->feed->cb.ts(data, 188, NULL, 0, - &dec->video_filter->feed->feed.ts); + &dec->video_filter->feed->feed.ts, NULL); return 0; } @@ -490,7 +490,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) if (output_pva) { dec->video_filter->feed->cb.ts(pva, length, NULL, 0, - &dec->video_filter->feed->feed.ts); + &dec->video_filter->feed->feed.ts, NULL); return; } @@ -551,7 +551,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) case 0x02: /* MainAudioStream */ if (output_pva) { dec->audio_filter->feed->cb.ts(pva, length, NULL, 0, - &dec->audio_filter->feed->feed.ts); + &dec->audio_filter->feed->feed.ts, NULL); return; } @@ -589,7 +589,7 @@ static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet, if (filter) filter->feed->cb.sec(&packet[2], length - 2, NULL, 0, - &filter->filter); + &filter->filter, NULL); } static void ttusb_dec_process_packet(struct ttusb_dec *dec) diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 127f8a0c098b..5095c380b2c1 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -112,6 +112,8 @@ static int usbtv_probe(struct usb_interface *intf, return 0; usbtv_audio_fail: + /* we must not free at this point */ + usb_get_dev(usbtv->udev); usbtv_video_free(usbtv); usbtv_video_fail: @@ -145,6 +147,7 @@ static void usbtv_disconnect(struct usb_interface *intf) static const struct usb_device_id usbtv_id_table[] = { { USB_DEVICE(0x1b71, 0x3002) }, { USB_DEVICE(0x1f71, 0x3301) }, + { USB_DEVICE(0x1f71, 0x3306) }, {} }; MODULE_DEVICE_TABLE(usb, usbtv_id_table); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 20397aba6849..102594ec3e97 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -366,10 +366,10 @@ static struct uvc_menu_info exposure_auto_controls[] = { { 8, "Aperture Priority Mode" }, }; -static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, - __u8 query, const __u8 *data) +static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + u8 query, const u8 *data) { - __s8 zoom = (__s8)data[0]; + s8 zoom = (s8)data[0]; switch (query) { case UVC_GET_CUR: @@ -385,17 +385,17 @@ static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, } static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, - __s32 value, __u8 *data) + s32 value, u8 *data) { data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; data[2] = min((int)abs(value), 0xff); } -static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, - __u8 query, const __u8 *data) +static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, + u8 query, const u8 *data) { unsigned int first = mapping->offset / 8; - __s8 rel = (__s8)data[first]; + s8 rel = (s8)data[first]; switch (query) { case UVC_GET_CUR: @@ -412,7 +412,7 @@ static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, } static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, - __s32 value, __u8 *data) + s32 value, u8 *data) { unsigned int first = mapping->offset / 8; @@ -745,17 +745,17 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { * Utility functions */ -static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) +static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) { return ctrl->uvc_data + id * ctrl->info.size; } -static inline int uvc_test_bit(const __u8 *data, int bit) +static inline int uvc_test_bit(const u8 *data, int bit) { return (data[bit >> 3] >> (bit & 7)) & 1; } -static inline void uvc_clear_bit(__u8 *data, int bit) +static inline void uvc_clear_bit(u8 *data, int bit) { data[bit >> 3] &= ~(1 << (bit & 7)); } @@ -765,20 +765,20 @@ static inline void uvc_clear_bit(__u8 *data, int bit) * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. */ -static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, - __u8 query, const __u8 *data) +static s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + u8 query, const u8 *data) { int bits = mapping->size; int offset = mapping->offset; - __s32 value = 0; - __u8 mask; + s32 value = 0; + u8 mask; data += offset / 8; offset &= 7; mask = ((1LL << bits) - 1) << offset; for (; bits > 0; data++) { - __u8 byte = *data & mask; + u8 byte = *data & mask; value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); bits -= 8 - (offset > 0 ? offset : 0); offset -= 8; @@ -796,11 +796,11 @@ static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, * in the little-endian data stored at 'data' to the value 'value'. */ static void uvc_set_le_value(struct uvc_control_mapping *mapping, - __s32 value, __u8 *data) + s32 value, u8 *data) { int bits = mapping->size; int offset = mapping->offset; - __u8 mask; + u8 mask; /* According to the v4l2 spec, writing any value to a button control * should result in the action belonging to the button control being @@ -826,13 +826,13 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, * Terminal and unit management */ -static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; -static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; -static const __u8 uvc_media_transport_input_guid[16] = +static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; +static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const u8 uvc_media_transport_input_guid[16] = UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; static int uvc_entity_match_guid(const struct uvc_entity *entity, - const __u8 guid[16]) + const u8 guid[16]) { switch (UVC_ENTITY_TYPE(entity)) { case UVC_ITT_CAMERA: @@ -857,7 +857,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity, * UVC Controls */ -static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, +static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id, struct uvc_control_mapping **mapping, struct uvc_control **control, int next) { @@ -890,7 +890,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, } static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, - __u32 v4l2_id, struct uvc_control_mapping **mapping) + u32 v4l2_id, struct uvc_control_mapping **mapping) { struct uvc_control *ctrl = NULL; struct uvc_entity *entity; @@ -1019,10 +1019,10 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct uvc_menu_info *menu; unsigned int i; - memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); + memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl)); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; - strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + strlcpy(v4l2_ctrl->name, mapping->name, sizeof(v4l2_ctrl->name)); v4l2_ctrl->flags = 0; if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) @@ -1182,7 +1182,7 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, } } - strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); + strlcpy(query_menu->name, menu_info->name, sizeof(query_menu->name)); done: mutex_unlock(&chain->ctrl_mutex); @@ -1590,6 +1590,36 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, * Dynamic controls */ +/* + * Retrieve flags for a given control + */ +static int uvc_ctrl_get_flags(struct uvc_device *dev, + const struct uvc_control *ctrl, + struct uvc_control_info *info) +{ + u8 *data; + int ret; + + data = kmalloc(1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, + info->selector, data, 1); + if (!ret) + info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF + | (data[0] & UVC_CONTROL_CAP_GET ? + UVC_CTRL_FLAG_GET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_SET ? + UVC_CTRL_FLAG_SET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? + UVC_CTRL_FLAG_AUTO_UPDATE : 0); + + kfree(data); + return ret; +} + static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, const struct uvc_control *ctrl, struct uvc_control_info *info) { @@ -1659,25 +1689,14 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, info->size = le16_to_cpup((__le16 *)data); - /* Query the control information (GET_INFO) */ - ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, - info->selector, data, 1); + ret = uvc_ctrl_get_flags(dev, ctrl, info); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, - "GET_INFO failed on control %pUl/%u (%d).\n", + "Failed to get flags for control %pUl/%u (%d).\n", info->entity, info->selector, ret); goto done; } - info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX - | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF - | (data[0] & UVC_CONTROL_CAP_GET ? - UVC_CTRL_FLAG_GET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_SET ? - UVC_CTRL_FLAG_SET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); - uvc_ctrl_fixup_xu_info(dev, ctrl, info); uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " @@ -1723,9 +1742,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_entity *entity; struct uvc_control *ctrl; unsigned int i, found = 0; - __u32 reqflags; - __u16 size; - __u8 *data = NULL; + u32 reqflags; + u16 size; + u8 *data = NULL; int ret; /* Find the extension unit. */ @@ -1902,6 +1921,13 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, goto done; } + /* + * Retrieve control flags from the device. Ignore errors and work with + * default flag values from the uvc_ctrl array when the device doesn't + * properly implement GET_INFO on standard controls. + */ + uvc_ctrl_get_flags(dev, ctrl, &ctrl->info); + ctrl->initialized = 1; uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " @@ -2150,7 +2176,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) list_for_each_entry(entity, &dev->entities, list) { struct uvc_control *ctrl; unsigned int bControlSize = 0, ncontrols; - __u8 *bmControls = NULL; + u8 *bmControls = NULL; if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { bmControls = entity->extension.bmControls; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index fd387bf3f02d..2469b49b2b30 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -216,7 +216,7 @@ static struct uvc_format_desc uvc_fmts[] = { */ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, - __u8 epaddr) + u8 epaddr) { struct usb_host_endpoint *ep; unsigned int i; @@ -230,7 +230,7 @@ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, return NULL; } -static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) +static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) { unsigned int len = ARRAY_SIZE(uvc_fmts); unsigned int i; @@ -243,9 +243,9 @@ static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) return NULL; } -static __u32 uvc_colorspace(const __u8 primaries) +static u32 uvc_colorspace(const u8 primaries) { - static const __u8 colorprimaries[] = { + static const u8 colorprimaries[] = { 0, V4L2_COLORSPACE_SRGB, V4L2_COLORSPACE_470_SYSTEM_M, @@ -267,14 +267,14 @@ static __u32 uvc_colorspace(const __u8 primaries) * continued fraction decomposition. Using 8 and 333 for n_terms and threshold * respectively seems to give nice results. */ -void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, +void uvc_simplify_fraction(u32 *numerator, u32 *denominator, unsigned int n_terms, unsigned int threshold) { - uint32_t *an; - uint32_t x, y, r; + u32 *an; + u32 x, y, r; unsigned int i, n; - an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); + an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL); if (an == NULL) return; @@ -318,21 +318,21 @@ void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, * to compute numerator / denominator * 10000000 using 32 bit fixed point * arithmetic only. */ -uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) +u32 uvc_fraction_to_interval(u32 numerator, u32 denominator) { - uint32_t multiplier; + u32 multiplier; /* Saturate the result if the operation would overflow. */ if (denominator == 0 || - numerator/denominator >= ((uint32_t)-1)/10000000) - return (uint32_t)-1; + numerator/denominator >= ((u32)-1)/10000000) + return (u32)-1; /* Divide both the denominator and the multiplier by two until * numerator * multiplier doesn't overflow. If anyone knows a better * algorithm please let me know. */ multiplier = 10000000; - while (numerator > ((uint32_t)-1)/multiplier) { + while (numerator > ((u32)-1)/multiplier) { multiplier /= 2; denominator /= 2; } @@ -391,7 +391,7 @@ static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) static int uvc_parse_format(struct uvc_device *dev, struct uvc_streaming *streaming, struct uvc_format *format, - __u32 **intervals, unsigned char *buffer, int buflen) + u32 **intervals, unsigned char *buffer, int buflen) { struct usb_interface *intf = streaming->intf; struct usb_host_interface *alts = intf->cur_altsetting; @@ -401,7 +401,7 @@ static int uvc_parse_format(struct uvc_device *dev, unsigned int width_multiplier = 1; unsigned int interval; unsigned int i, n; - __u8 ftype; + u8 ftype; format->type = buffer[2]; format->index = buffer[3]; @@ -423,7 +423,7 @@ static int uvc_parse_format(struct uvc_device *dev, if (fmtdesc != NULL) { strlcpy(format->name, fmtdesc->name, - sizeof format->name); + sizeof(format->name)); format->fcc = fmtdesc->fcc; } else { uvc_printk(KERN_INFO, "Unknown video format %pUl\n", @@ -466,7 +466,7 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - strlcpy(format->name, "MJPEG", sizeof format->name); + strlcpy(format->name, "MJPEG", sizeof(format->name)); format->fcc = V4L2_PIX_FMT_MJPEG; format->flags = UVC_FMT_FLAG_COMPRESSED; format->bpp = 0; @@ -484,13 +484,13 @@ static int uvc_parse_format(struct uvc_device *dev, switch (buffer[8] & 0x7f) { case 0: - strlcpy(format->name, "SD-DV", sizeof format->name); + strlcpy(format->name, "SD-DV", sizeof(format->name)); break; case 1: - strlcpy(format->name, "SDL-DV", sizeof format->name); + strlcpy(format->name, "SDL-DV", sizeof(format->name)); break; case 2: - strlcpy(format->name, "HD-DV", sizeof format->name); + strlcpy(format->name, "HD-DV", sizeof(format->name)); break; default: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " @@ -501,7 +501,7 @@ static int uvc_parse_format(struct uvc_device *dev, } strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", - sizeof format->name); + sizeof(format->name)); format->fcc = V4L2_PIX_FMT_DV; format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; @@ -510,7 +510,7 @@ static int uvc_parse_format(struct uvc_device *dev, /* Create a dummy frame descriptor. */ frame = &format->frame[0]; - memset(&format->frame[0], 0, sizeof format->frame[0]); + memset(&format->frame[0], 0, sizeof(format->frame[0])); frame->bFrameIntervalType = 1; frame->dwDefaultFrameInterval = 1; frame->dwFrameInterval = *intervals; @@ -658,8 +658,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, int _buflen, buflen = alts->extralen; unsigned int nformats = 0, nframes = 0, nintervals = 0; unsigned int size, i, n, p; - __u32 *interval; - __u16 psize; + u32 *interval; + u16 psize; int ret = -EINVAL; if (intf->cur_altsetting->desc.bInterfaceSubClass @@ -677,7 +677,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, return -EINVAL; } - streaming = kzalloc(sizeof *streaming, GFP_KERNEL); + streaming = kzalloc(sizeof(*streaming), GFP_KERNEL); if (streaming == NULL) { usb_driver_release_interface(&uvc_driver.driver, intf); return -EINVAL; @@ -827,8 +827,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, goto error; } - size = nformats * sizeof *format + nframes * sizeof *frame - + nintervals * sizeof *interval; + size = nformats * sizeof(*format) + nframes * sizeof(*frame) + + nintervals * sizeof(*interval); format = kzalloc(size, GFP_KERNEL); if (format == NULL) { ret = -ENOMEM; @@ -836,7 +836,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, } frame = (struct uvc_frame *)&format[nformats]; - interval = (__u32 *)&frame[nframes]; + interval = (u32 *)&frame[nframes]; streaming->format = format; streaming->nformats = nformats; @@ -930,7 +930,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; entity->bNrInPins = num_inputs; - entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); + entity->baSourceID = (u8 *)(&entity->pads[num_pads]); return entity; } @@ -995,14 +995,14 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, unit->extension.bNumControls = buffer[20]; memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); - unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + unit->extension.bmControls = (u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) usb_string(udev, buffer[24+p+2*n], unit->name, - sizeof unit->name); + sizeof(unit->name)); else sprintf(unit->name, "Extension %u", buffer[3]); @@ -1022,7 +1022,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, struct usb_interface *intf; struct usb_host_interface *alts = dev->intf->cur_altsetting; unsigned int i, n, p, len; - __u16 type; + u16 type; switch (buffer[2]) { case UVC_VC_HEADER: @@ -1101,7 +1101,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; - term->camera.bmControls = (__u8 *)term + sizeof *term; + term->camera.bmControls = (u8 *)term + sizeof(*term); term->camera.wObjectiveFocalLengthMin = get_unaligned_le16(&buffer[8]); term->camera.wObjectiveFocalLengthMax = @@ -1112,17 +1112,17 @@ static int uvc_parse_standard_control(struct uvc_device *dev, } else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; - term->media.bmControls = (__u8 *)term + sizeof *term; + term->media.bmControls = (u8 *)term + sizeof(*term); term->media.bTransportModeSize = p; - term->media.bmTransportModes = (__u8 *)term - + sizeof *term + n; + term->media.bmTransportModes = (u8 *)term + + sizeof(*term) + n; memcpy(term->media.bmControls, &buffer[9], n); memcpy(term->media.bmTransportModes, &buffer[10+n], p); } if (buffer[7] != 0) usb_string(udev, buffer[7], term->name, - sizeof term->name); + sizeof(term->name)); else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) sprintf(term->name, "Camera %u", buffer[3]); else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) @@ -1162,7 +1162,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (buffer[8] != 0) usb_string(udev, buffer[8], term->name, - sizeof term->name); + sizeof(term->name)); else sprintf(term->name, "Output %u", buffer[3]); @@ -1187,7 +1187,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (buffer[5+p] != 0) usb_string(udev, buffer[5+p], unit->name, - sizeof unit->name); + sizeof(unit->name)); else sprintf(unit->name, "Selector %u", buffer[3]); @@ -1213,14 +1213,14 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->processing.wMaxMultiplier = get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; - unit->processing.bmControls = (__u8 *)unit + sizeof *unit; + unit->processing.bmControls = (u8 *)unit + sizeof(*unit); memcpy(unit->processing.bmControls, &buffer[8], n); if (dev->uvc_version >= 0x0110) unit->processing.bmVideoStandards = buffer[9+n]; if (buffer[8+n] != 0) usb_string(udev, buffer[8+n], unit->name, - sizeof unit->name); + sizeof(unit->name)); else sprintf(unit->name, "Processing %u", buffer[3]); @@ -1246,12 +1246,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->extension.bNumControls = buffer[20]; memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit; + unit->extension.bmControls = (u8 *)unit + sizeof(*unit); memcpy(unit->extension.bmControls, &buffer[23+p], n); if (buffer[23+p+n] != 0) usb_string(udev, buffer[23+p+n], unit->name, - sizeof unit->name); + sizeof(unit->name)); else sprintf(unit->name, "Extension %u", buffer[3]); @@ -1936,7 +1936,7 @@ int uvc_register_video_device(struct uvc_device *dev, break; } - strlcpy(vdev->name, dev->name, sizeof vdev->name); + strlcpy(vdev->name, dev->name, sizeof(vdev->name)); /* * Set the driver data before calling video_register_device, otherwise @@ -2070,7 +2070,8 @@ static int uvc_probe(struct usb_interface *intf, udev->devpath); /* Allocate memory for the device and initialize it. */ - if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) return -ENOMEM; INIT_LIST_HEAD(&dev->entities); @@ -2089,9 +2090,9 @@ static int uvc_probe(struct usb_interface *intf, dev->meta_format = info->meta_format; if (udev->product != NULL) - strlcpy(dev->name, udev->product, sizeof dev->name); + strlcpy(dev->name, udev->product, sizeof(dev->name)); else - snprintf(dev->name, sizeof dev->name, + snprintf(dev->name, sizeof(dev->name), "UVC Camera (%04x:%04x)", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c index 5059fbf41020..81e6f2187bfb 100644 --- a/drivers/media/usb/uvc/uvc_isight.c +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -37,16 +37,16 @@ */ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, - const __u8 *data, unsigned int len) + const u8 *data, unsigned int len) { - static const __u8 hdr[] = { + static const u8 hdr[] = { 0x11, 0x22, 0x33, 0x44, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xfa, 0xce }; unsigned int maxlen, nbytes; - __u8 *mem; + u8 *mem; int is_header = 0; if (buf == NULL) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 1ef20e74b7ac..7b710410584a 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -78,7 +78,7 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, /* -------------------------------------------------------------------------- * Status interrupt endpoint */ -static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) +static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) { if (len < 3) { uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " @@ -99,7 +99,7 @@ static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) } } -static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) +static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) { char *attrs[3] = { "value", "info", "failure" }; diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index a13ad4e178be..bd32914259ae 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -40,13 +40,13 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, unsigned int size; int ret; - map = kzalloc(sizeof *map, GFP_KERNEL); + map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) return -ENOMEM; map->id = xmap->id; - memcpy(map->name, xmap->name, sizeof map->name); - memcpy(map->entity, xmap->entity, sizeof map->entity); + memcpy(map->name, xmap->name, sizeof(map->name)); + memcpy(map->entity, xmap->entity, sizeof(map->entity)); map->selector = xmap->selector; map->size = xmap->size; map->offset = xmap->offset; @@ -105,12 +105,12 @@ free_map: * the Video Probe and Commit negotiation, but some hardware don't implement * that feature. */ -static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) +static u32 uvc_try_frame_interval(struct uvc_frame *frame, u32 interval) { unsigned int i; if (frame->bFrameIntervalType) { - __u32 best = -1, dist; + u32 best = -1, dist; for (i = 0; i < frame->bFrameIntervalType; ++i) { dist = interval > frame->dwFrameInterval[i] @@ -125,9 +125,9 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) interval = frame->dwFrameInterval[i-1]; } else { - const __u32 min = frame->dwFrameInterval[0]; - const __u32 max = frame->dwFrameInterval[1]; - const __u32 step = frame->dwFrameInterval[2]; + const u32 min = frame->dwFrameInterval[0]; + const u32 max = frame->dwFrameInterval[1]; + const u32 step = frame->dwFrameInterval[2]; interval = min + (interval - min + step/2) / step * step; if (interval > max) @@ -137,7 +137,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } -static __u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, +static u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, const struct uvc_frame *frame) { switch (format->fcc) { @@ -158,17 +158,17 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, { struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; - __u16 rw, rh; + u16 rw, rh; unsigned int d, maxd; unsigned int i; - __u32 interval; + u32 interval; int ret = 0; - __u8 *fcc; + u8 *fcc; if (fmt->type != stream->type) return -EINVAL; - fcc = (__u8 *)&fmt->fmt.pix.pixelformat; + fcc = (u8 *)&fmt->fmt.pix.pixelformat; uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", fmt->fmt.pix.pixelformat, fcc[0], fcc[1], fcc[2], fcc[3], @@ -197,8 +197,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, maxd = (unsigned int)-1; for (i = 0; i < format->nframes; ++i) { - __u16 w = format->frame[i].wWidth; - __u16 h = format->frame[i].wHeight; + u16 w = format->frame[i].wWidth; + u16 h = format->frame[i].wHeight; d = min(w, rw) * min(h, rh); d = w*h + rw*rh - 2*d; @@ -224,7 +224,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, (100000000/interval)%10); /* Set the format index, frame index and frame interval. */ - memset(probe, 0, sizeof *probe); + memset(probe, 0, sizeof(*probe)); probe->bmHint = 1; /* dwFrameInterval */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; @@ -336,7 +336,7 @@ done: static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { - uint32_t numerator, denominator; + u32 numerator, denominator; if (parm->type != stream->type) return -EINVAL; @@ -348,7 +348,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, denominator = 10000000; uvc_simplify_fraction(&numerator, &denominator, 8, 333); - memset(parm, 0, sizeof *parm); + memset(parm, 0, sizeof(*parm)); parm->type = stream->type; if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -373,7 +373,10 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, { struct uvc_streaming_control probe; struct v4l2_fract timeperframe; - uint32_t interval; + struct uvc_format *format; + struct uvc_frame *frame; + u32 interval, maxd; + unsigned int i; int ret; if (parm->type != stream->type) @@ -396,9 +399,33 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, return -EBUSY; } + format = stream->cur_format; + frame = stream->cur_frame; probe = stream->ctrl; - probe.dwFrameInterval = - uvc_try_frame_interval(stream->cur_frame, interval); + probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); + maxd = abs((s32)probe.dwFrameInterval - interval); + + /* Try frames with matching size to find the best frame interval. */ + for (i = 0; i < format->nframes && maxd != 0; i++) { + u32 d, ival; + + if (&format->frame[i] == stream->cur_frame) + continue; + + if (format->frame[i].wWidth != stream->cur_frame->wWidth || + format->frame[i].wHeight != stream->cur_frame->wHeight) + continue; + + ival = uvc_try_frame_interval(&format->frame[i], interval); + d = abs((s32)ival - interval); + if (d >= maxd) + continue; + + frame = &format->frame[i]; + probe.bFrameIndex = frame->bFrameIndex; + probe.dwFrameInterval = ival; + maxd = d; + } /* Probe the device with the new settings. */ ret = uvc_probe_video(stream, &probe); @@ -408,6 +435,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, } stream->ctrl = probe; + stream->cur_frame = frame; mutex_unlock(&stream->mutex); /* Return the actual frame period. */ @@ -498,7 +526,7 @@ static int uvc_v4l2_open(struct file *file) return ret; /* Create the device handle. */ - handle = kzalloc(sizeof *handle, GFP_KERNEL); + handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); return -ENOMEM; @@ -577,7 +605,7 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, { struct uvc_format *format; enum v4l2_buf_type type = fmt->type; - __u32 index = fmt->index; + u32 index = fmt->index; if (fmt->type != stream->type || fmt->index >= stream->nformats) return -EINVAL; @@ -1145,8 +1173,9 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh, struct uvc_fh *handle = fh; struct uvc_streaming *stream = handle->stream; struct uvc_format *format = NULL; - struct uvc_frame *frame; - int i; + struct uvc_frame *frame = NULL; + unsigned int index; + unsigned int i; /* Look for the given pixel format */ for (i = 0; i < stream->nformats; i++) { @@ -1158,10 +1187,20 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh, if (format == NULL) return -EINVAL; - if (fsize->index >= format->nframes) + /* Skip duplicate frame sizes */ + for (i = 0, index = 0; i < format->nframes; i++) { + if (frame && frame->wWidth == format->frame[i].wWidth && + frame->wHeight == format->frame[i].wHeight) + continue; + frame = &format->frame[i]; + if (index == fsize->index) + break; + index++; + } + + if (i == format->nframes) return -EINVAL; - frame = &format->frame[fsize->index]; fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = frame->wWidth; fsize->discrete.height = frame->wHeight; @@ -1175,7 +1214,9 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh, struct uvc_streaming *stream = handle->stream; struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; - int i; + unsigned int nintervals; + unsigned int index; + unsigned int i; /* Look for the given pixel format and frame size */ for (i = 0; i < stream->nformats; i++) { @@ -1187,30 +1228,28 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh, if (format == NULL) return -EINVAL; + index = fival->index; for (i = 0; i < format->nframes; i++) { if (format->frame[i].wWidth == fival->width && format->frame[i].wHeight == fival->height) { frame = &format->frame[i]; - break; + nintervals = frame->bFrameIntervalType ?: 1; + if (index < nintervals) + break; + index -= nintervals; } } - if (frame == NULL) + if (i == format->nframes) return -EINVAL; if (frame->bFrameIntervalType) { - if (fival->index >= frame->bFrameIntervalType) - return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; fival->discrete.numerator = - frame->dwFrameInterval[fival->index]; + frame->dwFrameInterval[index]; fival->discrete.denominator = 10000000; uvc_simplify_fraction(&fival->discrete.numerator, &fival->discrete.denominator, 8, 333); } else { - if (fival->index) - return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; fival->stepwise.min.numerator = frame->dwFrameInterval[0]; fival->stepwise.min.denominator = 10000000; @@ -1261,20 +1300,20 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio, #ifdef CONFIG_COMPAT struct uvc_xu_control_mapping32 { - __u32 id; - __u8 name[32]; - __u8 entity[16]; - __u8 selector; + u32 id; + u8 name[32]; + u8 entity[16]; + u8 selector; - __u8 size; - __u8 offset; - __u32 v4l2_type; - __u32 data_type; + u8 size; + u8 offset; + u32 v4l2_type; + u32 data_type; compat_caddr_t menu_info; - __u32 menu_count; + u32 menu_count; - __u32 reserved[4]; + u32 reserved[4]; }; static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, @@ -1310,10 +1349,10 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, } struct uvc_xu_control_query32 { - __u8 unit; - __u8 selector; - __u8 query; - __u16 size; + u8 unit; + u8 selector; + u8 query; + u16 size; compat_caddr_t data; }; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 5441553f74e1..aa0082fe5833 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -30,11 +30,11 @@ * UVC Controls */ -static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size, +static int __uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, + u8 intfnum, u8 cs, void *data, u16 size, int timeout) { - __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; unsigned int pipe; pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) @@ -45,7 +45,7 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, unit << 8 | intfnum, data, size, timeout); } -static const char *uvc_query_name(__u8 query) +static const char *uvc_query_name(u8 query) { switch (query) { case UVC_SET_CUR: @@ -69,8 +69,8 @@ static const char *uvc_query_name(__u8 query) } } -int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size) +int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, + u8 intfnum, u8 cs, void *data, u16 size) { int ret; @@ -164,10 +164,10 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, } static int uvc_get_video_ctrl(struct uvc_streaming *stream, - struct uvc_streaming_control *ctrl, int probe, __u8 query) + struct uvc_streaming_control *ctrl, int probe, u8 query) { - __u8 *data; - __u16 size; + u8 *data; + u16 size; int ret; size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; @@ -191,7 +191,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " "compliance - GET_MIN/MAX(PROBE) incorrectly " "supported. Enabling workaround.\n"); - memset(ctrl, 0, sizeof *ctrl); + memset(ctrl, 0, sizeof(*ctrl)); ctrl->wCompQuality = le16_to_cpup((__le16 *)data); ret = 0; goto out; @@ -254,8 +254,8 @@ out: static int uvc_set_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe) { - __u8 *data; - __u16 size; + u8 *data; + u16 size; int ret; size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; @@ -301,7 +301,7 @@ int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { struct uvc_streaming_control probe_min, probe_max; - __u16 bandwidth; + u16 bandwidth; unsigned int i; int ret; @@ -379,7 +379,7 @@ static inline ktime_t uvc_video_get_time(void) static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, - const __u8 *data, int len) + const u8 *data, int len) { struct uvc_clock_sample *sample; unsigned int header_size; @@ -705,7 +705,7 @@ done: */ static void uvc_video_stats_decode(struct uvc_streaming *stream, - const __u8 *data, int len) + const u8 *data, int len) { unsigned int header_size; bool has_pts = false; @@ -946,9 +946,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) + struct uvc_buffer *buf, const u8 *data, int len) { - __u8 fid; + u8 fid; /* Sanity checks: * - packet must be at least 2 bytes long @@ -1009,7 +1009,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, buf->buf.field = V4L2_FIELD_NONE; buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = uvc_video_get_time(); + buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; @@ -1043,7 +1043,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, } static void uvc_video_decode_data(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) + struct uvc_buffer *buf, const u8 *data, int len) { unsigned int maxlen, nbytes; void *mem; @@ -1067,7 +1067,7 @@ static void uvc_video_decode_data(struct uvc_streaming *stream, } static void uvc_video_decode_end(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) + struct uvc_buffer *buf, const u8 *data, int len) { /* Mark the buffer as done if the EOF marker is set. */ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { @@ -1092,7 +1092,7 @@ static void uvc_video_decode_end(struct uvc_streaming *stream, * video buffer to the transfer buffer. */ static int uvc_video_encode_header(struct uvc_streaming *stream, - struct uvc_buffer *buf, __u8 *data, int len) + struct uvc_buffer *buf, u8 *data, int len) { data[0] = 2; /* Header length */ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF @@ -1101,7 +1101,7 @@ static int uvc_video_encode_header(struct uvc_streaming *stream, } static int uvc_video_encode_data(struct uvc_streaming *stream, - struct uvc_buffer *buf, __u8 *data, int len) + struct uvc_buffer *buf, u8 *data, int len) { struct uvc_video_queue *queue = &stream->queue; unsigned int nbytes; @@ -1191,7 +1191,8 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, uvc_trace(UVC_TRACE_FRAME, "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n", - __func__, time, meta->sof, meta->length, meta->flags, + __func__, ktime_to_ns(time), meta->sof, meta->length, + meta->flags, has_pts ? *(u32 *)meta->buf : 0, has_scr ? *(u32 *)scr : 0, has_scr ? *(u32 *)(scr + 4) & 0x7ff : 0); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index d9e7c70788d0..be5cf179228b 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -208,60 +208,60 @@ struct uvc_device; struct uvc_control_info { struct list_head mappings; - __u8 entity[16]; - __u8 index; /* Bit index in bmControls */ - __u8 selector; + u8 entity[16]; + u8 index; /* Bit index in bmControls */ + u8 selector; - __u16 size; - __u32 flags; + u16 size; + u32 flags; }; struct uvc_control_mapping { struct list_head list; struct list_head ev_subs; - __u32 id; - __u8 name[32]; - __u8 entity[16]; - __u8 selector; + u32 id; + u8 name[32]; + u8 entity[16]; + u8 selector; - __u8 size; - __u8 offset; + u8 size; + u8 offset; enum v4l2_ctrl_type v4l2_type; - __u32 data_type; + u32 data_type; struct uvc_menu_info *menu_info; - __u32 menu_count; + u32 menu_count; - __u32 master_id; - __s32 master_manual; - __u32 slave_ids[2]; + u32 master_id; + s32 master_manual; + u32 slave_ids[2]; - __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, - const __u8 *data); - void (*set) (struct uvc_control_mapping *mapping, __s32 value, - __u8 *data); + s32 (*get)(struct uvc_control_mapping *mapping, u8 query, + const u8 *data); + void (*set)(struct uvc_control_mapping *mapping, s32 value, + u8 *data); }; struct uvc_control { struct uvc_entity *entity; struct uvc_control_info info; - __u8 index; /* Used to match the uvc_control entry with a + u8 index; /* Used to match the uvc_control entry with a uvc_control_info. */ - __u8 dirty:1, - loaded:1, - modified:1, - cached:1, - initialized:1; + u8 dirty:1, + loaded:1, + modified:1, + cached:1, + initialized:1; - __u8 *uvc_data; + u8 *uvc_data; }; struct uvc_format_desc { char *name; - __u8 guid[16]; - __u32 fcc; + u8 guid[16]; + u32 fcc; }; /* The term 'entity' refers to both UVC units and UVC terminals. @@ -287,8 +287,8 @@ struct uvc_entity { * chain. */ unsigned int flags; - __u8 id; - __u16 type; + u8 id; + u16 type; char name[64]; /* Media controller-related fields. */ @@ -300,69 +300,69 @@ struct uvc_entity { union { struct { - __u16 wObjectiveFocalLengthMin; - __u16 wObjectiveFocalLengthMax; - __u16 wOcularFocalLength; - __u8 bControlSize; - __u8 *bmControls; + u16 wObjectiveFocalLengthMin; + u16 wObjectiveFocalLengthMax; + u16 wOcularFocalLength; + u8 bControlSize; + u8 *bmControls; } camera; struct { - __u8 bControlSize; - __u8 *bmControls; - __u8 bTransportModeSize; - __u8 *bmTransportModes; + u8 bControlSize; + u8 *bmControls; + u8 bTransportModeSize; + u8 *bmTransportModes; } media; struct { } output; struct { - __u16 wMaxMultiplier; - __u8 bControlSize; - __u8 *bmControls; - __u8 bmVideoStandards; + u16 wMaxMultiplier; + u8 bControlSize; + u8 *bmControls; + u8 bmVideoStandards; } processing; struct { } selector; struct { - __u8 guidExtensionCode[16]; - __u8 bNumControls; - __u8 bControlSize; - __u8 *bmControls; - __u8 *bmControlsType; + u8 guidExtensionCode[16]; + u8 bNumControls; + u8 bControlSize; + u8 *bmControls; + u8 *bmControlsType; } extension; }; - __u8 bNrInPins; - __u8 *baSourceID; + u8 bNrInPins; + u8 *baSourceID; unsigned int ncontrols; struct uvc_control *controls; }; struct uvc_frame { - __u8 bFrameIndex; - __u8 bmCapabilities; - __u16 wWidth; - __u16 wHeight; - __u32 dwMinBitRate; - __u32 dwMaxBitRate; - __u32 dwMaxVideoFrameBufferSize; - __u8 bFrameIntervalType; - __u32 dwDefaultFrameInterval; - __u32 *dwFrameInterval; + u8 bFrameIndex; + u8 bmCapabilities; + u16 wWidth; + u16 wHeight; + u32 dwMinBitRate; + u32 dwMaxBitRate; + u32 dwMaxVideoFrameBufferSize; + u8 bFrameIntervalType; + u32 dwDefaultFrameInterval; + u32 *dwFrameInterval; }; struct uvc_format { - __u8 type; - __u8 index; - __u8 bpp; - __u8 colorspace; - __u32 fcc; - __u32 flags; + u8 type; + u8 index; + u8 bpp; + u8 colorspace; + u32 fcc; + u32 flags; char name[32]; @@ -371,16 +371,16 @@ struct uvc_format { }; struct uvc_streaming_header { - __u8 bNumFormats; - __u8 bEndpointAddress; - __u8 bTerminalLink; - __u8 bControlSize; - __u8 *bmaControls; + u8 bNumFormats; + u8 bEndpointAddress; + u8 bTerminalLink; + u8 bControlSize; + u8 *bmaControls; /* The following fields are used by input headers only. */ - __u8 bmInfo; - __u8 bStillCaptureMethod; - __u8 bTriggerSupport; - __u8 bTriggerUsage; + u8 bmInfo; + u8 bStillCaptureMethod; + u8 bTriggerSupport; + u8 bTriggerUsage; }; enum uvc_buffer_state { @@ -490,7 +490,7 @@ struct uvc_streaming { struct usb_interface *intf; int intfnum; - __u16 maxpsize; + u16 maxpsize; struct uvc_streaming_header header; enum v4l2_buf_type type; @@ -517,16 +517,16 @@ struct uvc_streaming { struct { struct video_device vdev; struct uvc_video_queue queue; - __u32 format; + u32 format; } meta; /* Context data used by the bulk completion handler. */ struct { - __u8 header[256]; + u8 header[256]; unsigned int header_size; int skip_payload; - __u32 payload_size; - __u32 max_payload_size; + u32 payload_size; + u32 max_payload_size; } bulk; struct urb *urb[UVC_URBS]; @@ -534,8 +534,8 @@ struct uvc_streaming { dma_addr_t urb_dma[UVC_URBS]; unsigned int urb_size; - __u32 sequence; - __u8 last_fid; + u32 sequence; + u8 last_fid; /* debugfs */ struct dentry *debugfs_dir; @@ -570,8 +570,8 @@ struct uvc_device { struct usb_device *udev; struct usb_interface *intf; unsigned long warnings; - __u32 quirks; - __u32 meta_format; + u32 quirks; + u32 meta_format; int intfnum; char name[32]; @@ -584,8 +584,8 @@ struct uvc_device { struct media_device mdev; #endif struct v4l2_device vdev; - __u16 uvc_version; - __u32 clock_frequency; + u16 uvc_version; + u32 clock_frequency; struct list_head entities; struct list_head chains; @@ -597,7 +597,7 @@ struct uvc_device { /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; struct urb *int_urb; - __u8 *status; + u8 *status; struct input_dev *input; char input_phys[64]; }; @@ -667,40 +667,38 @@ extern unsigned int uvc_hw_timestamps_param; /* Core driver */ extern struct uvc_driver uvc_driver; -extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); +struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); /* Video buffers queue management. */ -extern int uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type, int drop_corrupted); -extern void uvc_queue_release(struct uvc_video_queue *queue); -extern int uvc_request_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb); -extern int uvc_query_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf); -extern int uvc_create_buffers(struct uvc_video_queue *queue, - struct v4l2_create_buffers *v4l2_cb); -extern int uvc_queue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf); -extern int uvc_export_buffer(struct uvc_video_queue *queue, - struct v4l2_exportbuffer *exp); -extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf, int nonblocking); -extern int uvc_queue_streamon(struct uvc_video_queue *queue, - enum v4l2_buf_type type); -extern int uvc_queue_streamoff(struct uvc_video_queue *queue, - enum v4l2_buf_type type); -extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); -extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf); -extern int uvc_queue_mmap(struct uvc_video_queue *queue, - struct vm_area_struct *vma); -extern __poll_t uvc_queue_poll(struct uvc_video_queue *queue, - struct file *file, poll_table *wait); +int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, + int drop_corrupted); +void uvc_queue_release(struct uvc_video_queue *queue); +int uvc_request_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb); +int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +int uvc_create_buffers(struct uvc_video_queue *queue, + struct v4l2_create_buffers *v4l2_cb); +int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +int uvc_export_buffer(struct uvc_video_queue *queue, + struct v4l2_exportbuffer *exp); +int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type); +int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type); +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); +int uvc_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma); +__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait); #ifndef CONFIG_MMU -extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff); +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff); #endif -extern int uvc_queue_allocated(struct uvc_video_queue *queue); +int uvc_queue_allocated(struct uvc_video_queue *queue); static inline int uvc_queue_streaming(struct uvc_video_queue *queue) { return vb2_is_streaming(&queue->queue); @@ -711,18 +709,18 @@ extern const struct v4l2_ioctl_ops uvc_ioctl_ops; extern const struct v4l2_file_operations uvc_fops; /* Media controller */ -extern int uvc_mc_register_entities(struct uvc_video_chain *chain); -extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); +int uvc_mc_register_entities(struct uvc_video_chain *chain); +void uvc_mc_cleanup_entity(struct uvc_entity *entity); /* Video */ -extern int uvc_video_init(struct uvc_streaming *stream); -extern int uvc_video_suspend(struct uvc_streaming *stream); -extern int uvc_video_resume(struct uvc_streaming *stream, int reset); -extern int uvc_video_enable(struct uvc_streaming *stream, int enable); -extern int uvc_probe_video(struct uvc_streaming *stream, - struct uvc_streaming_control *probe); -extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size); +int uvc_video_init(struct uvc_streaming *stream); +int uvc_video_suspend(struct uvc_streaming *stream); +int uvc_video_resume(struct uvc_streaming *stream, int reset); +int uvc_video_enable(struct uvc_streaming *stream, int enable); +int uvc_probe_video(struct uvc_streaming *stream, + struct uvc_streaming_control *probe); +int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, + u8 intfnum, u8 cs, void *data, u16 size); void uvc_video_clock_update(struct uvc_streaming *stream, struct vb2_v4l2_buffer *vbuf, struct uvc_buffer *buf); @@ -737,32 +735,32 @@ int uvc_register_video_device(struct uvc_device *dev, const struct v4l2_ioctl_ops *ioctl_ops); /* Status */ -extern int uvc_status_init(struct uvc_device *dev); -extern void uvc_status_cleanup(struct uvc_device *dev); -extern int uvc_status_start(struct uvc_device *dev, gfp_t flags); -extern void uvc_status_stop(struct uvc_device *dev); +int uvc_status_init(struct uvc_device *dev); +void uvc_status_cleanup(struct uvc_device *dev); +int uvc_status_start(struct uvc_device *dev, gfp_t flags); +void uvc_status_stop(struct uvc_device *dev); /* Controls */ extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; -extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct v4l2_queryctrl *v4l2_ctrl); -extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, - struct v4l2_querymenu *query_menu); - -extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, - const struct uvc_control_mapping *mapping); -extern int uvc_ctrl_init_device(struct uvc_device *dev); -extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); -extern int uvc_ctrl_restore_values(struct uvc_device *dev); - -extern int uvc_ctrl_begin(struct uvc_video_chain *chain); -extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, - const struct v4l2_ext_control *xctrls, - unsigned int xctrls_count); +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl); +int uvc_query_v4l2_menu(struct uvc_video_chain *chain, + struct v4l2_querymenu *query_menu); + +int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, + const struct uvc_control_mapping *mapping); +int uvc_ctrl_init_device(struct uvc_device *dev); +void uvc_ctrl_cleanup_device(struct uvc_device *dev); +int uvc_ctrl_restore_values(struct uvc_device *dev); + +int uvc_ctrl_begin(struct uvc_video_chain *chain); +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count); static inline int uvc_ctrl_commit(struct uvc_fh *handle, - const struct v4l2_ext_control *xctrls, - unsigned int xctrls_count) + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); } @@ -771,25 +769,23 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle) return __uvc_ctrl_commit(handle, 1, NULL, 0); } -extern int uvc_ctrl_get(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl); -extern int uvc_ctrl_set(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl); +int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, - struct uvc_xu_control_query *xqry); +int uvc_xu_ctrl_query(struct uvc_video_chain *chain, + struct uvc_xu_control_query *xqry); /* Utility functions */ -extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, - unsigned int n_terms, unsigned int threshold); -extern uint32_t uvc_fraction_to_interval(uint32_t numerator, - uint32_t denominator); -extern struct usb_host_endpoint *uvc_find_endpoint( - struct usb_host_interface *alts, __u8 epaddr); +void uvc_simplify_fraction(u32 *numerator, u32 *denominator, + unsigned int n_terms, unsigned int threshold); +u32 uvc_fraction_to_interval(u32 numerator, u32 denominator); +struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, + u8 epaddr); /* Quirks support */ void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf, struct uvc_buffer *meta_buf); + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf); /* debugfs and statistics */ void uvc_debugfs_init(void); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 8b7c19943d46..b8886102c5ed 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -517,8 +517,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); return; } - DBG("%s: Buffer 0x%08lx size= %d\n", __func__, - (unsigned long)vbuf, pos); + DBG("%s: Buffer %p size= %d\n", __func__, vbuf, pos); /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; @@ -1277,7 +1276,7 @@ static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) DBG("%s: cam == NULL\n", __func__); return -ENODEV; } - DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); + DBG("mmap called, vma=%p\n", vma); ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index bf52fbd07aed..8e37e7c5e0f7 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -7,6 +7,7 @@ config VIDEO_V4L2 tristate depends on (I2C || I2C=n) && VIDEO_DEV select RATIONAL + select VIDEOBUF2_V4L2 if VIDEOBUF2_CORE default (I2C || I2C=n) && VIDEO_DEV config VIDEO_ADV_DEBUG diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 80de2cb9c476..7df54582e956 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -13,7 +13,7 @@ ifeq ($(CONFIG_COMPAT),y) endif obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o ifeq ($(CONFIG_TRACEPOINTS),y) - videodev-objs += vb2-trace.o v4l2-trace.o + videodev-objs += v4l2-trace.o endif videodev-$(CONFIG_MEDIA_CONTROLLER) += v4l2-mc.o @@ -35,4 +35,3 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ccflags-y += -I$(srctree)/drivers/media/tuners - diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 82852f23a3b6..7f858c39753c 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1099,23 +1099,14 @@ static int tuner_s_radio(struct v4l2_subdev *sd) */ /** - * tuner_s_power - controls the power state of the tuner + * tuner_standby - places the tuner in standby mode * @sd: pointer to struct v4l2_subdev - * @on: a zero value puts the tuner to sleep, non-zero wakes it up */ -static int tuner_s_power(struct v4l2_subdev *sd, int on) +static int tuner_standby(struct v4l2_subdev *sd) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (on) { - if (t->standby && set_mode(t, t->mode) == 0) { - dprintk("Waking up tuner\n"); - set_freq(t, 0); - } - return 0; - } - dprintk("Putting tuner to sleep\n"); t->standby = true; if (analog_ops->standby) @@ -1328,10 +1319,10 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, - .s_power = tuner_s_power, }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { + .standby = tuner_standby, .s_radio = tuner_s_radio, .g_tuner = tuner_g_tuner, .s_tuner = tuner_s_tuner, diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 8650ad92b64d..b518b92d6d96 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -357,31 +357,35 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, } EXPORT_SYMBOL_GPL(v4l_bound_align_image); -const struct v4l2_frmsize_discrete * -v4l2_find_nearest_format(const struct v4l2_frmsize_discrete *sizes, - size_t num_sizes, - s32 width, s32 height) +const void * +__v4l2_find_nearest_size(const void *array, size_t array_size, + size_t entry_size, size_t width_offset, + size_t height_offset, s32 width, s32 height) { - int i; - u32 error, min_error = UINT_MAX; - const struct v4l2_frmsize_discrete *size, *best = NULL; + u32 error, min_error = U32_MAX; + const void *best = NULL; + unsigned int i; - if (!sizes) + if (!array) return NULL; - for (i = 0, size = sizes; i < num_sizes; i++, size++) { - error = abs(size->width - width) + abs(size->height - height); - if (error < min_error) { - min_error = error; - best = size; - } + for (i = 0; i < array_size; i++, array += entry_size) { + const u32 *entry_width = array + width_offset; + const u32 *entry_height = array + height_offset; + + error = abs(*entry_width - width) + abs(*entry_height - height); + if (error > min_error) + continue; + + min_error = error; + best = array; if (!error) break; } return best; } -EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); +EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size); void v4l2_get_timestamp(struct timeval *tv) { @@ -392,3 +396,51 @@ void v4l2_get_timestamp(struct timeval *tv) tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; } EXPORT_SYMBOL_GPL(v4l2_get_timestamp); + +int v4l2_g_parm_cap(struct video_device *vdev, + struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct v4l2_subdev_frame_interval ival = { 0 }; + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + if (vdev->device_caps & V4L2_CAP_READWRITE) + a->parm.capture.readbuffers = 2; + if (v4l2_subdev_has_op(sd, video, g_frame_interval)) + a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + ret = v4l2_subdev_call(sd, video, g_frame_interval, &ival); + if (!ret) + a->parm.capture.timeperframe = ival.interval; + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_g_parm_cap); + +int v4l2_s_parm_cap(struct video_device *vdev, + struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct v4l2_subdev_frame_interval ival = { + .interval = a->parm.capture.timeperframe + }; + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + memset(&a->parm, 0, sizeof(a->parm)); + if (vdev->device_caps & V4L2_CAP_READWRITE) + a->parm.capture.readbuffers = 2; + else + a->parm.capture.readbuffers = 0; + + if (v4l2_subdev_has_op(sd, video, g_frame_interval)) + a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + ret = v4l2_subdev_call(sd, video, s_frame_interval, &ival); + if (!ret) + a->parm.capture.timeperframe = ival.interval; + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_s_parm_cap); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index ce08b50b8290..d29e45516eb7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -480,6 +480,57 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const hevc_profile[] = { + "Main", + "Main Still Picture", + "Main 10", + NULL, + }; + static const char * const hevc_level[] = { + "1", + "2", + "2.1", + "3", + "3.1", + "4", + "4.1", + "5", + "5.1", + "5.2", + "6", + "6.1", + "6.2", + NULL, + }; + static const char * const hevc_hierarchial_coding_type[] = { + "B", + "P", + NULL, + }; + static const char * const hevc_refresh_type[] = { + "None", + "CRA", + "IDR", + NULL, + }; + static const char * const hevc_size_of_length_field[] = { + "0", + "1", + "2", + "4", + NULL, + }; + static const char * const hevc_tier[] = { + "Main", + "High", + NULL, + }; + static const char * const hevc_loop_filter_mode[] = { + "Disabled", + "Enabled", + "Disabled at slice boundary", + "NULL", + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -575,6 +626,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return dv_it_content_type; case V4L2_CID_DETECT_MD_MODE: return detect_md_mode; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + return hevc_profile; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + return hevc_level; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + return hevc_hierarchial_coding_type; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + return hevc_refresh_type; + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + return hevc_size_of_length_field; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + return hevc_tier; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + return hevc_loop_filter_mode; default: return NULL; @@ -776,6 +841,53 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; + /* HEVC controls */ + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: return "HEVC P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: return "HEVC B-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: return "HEVC Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: return "HEVC Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: return "HEVC Profile"; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: return "HEVC Level"; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: return "HEVC Tier"; + case V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION: return "HEVC Frame Rate Resolution"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH: return "HEVC Maximum Coding Unit Depth"; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: return "HEVC Refresh Type"; + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: return "HEVC Constant Intra Prediction"; + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: return "HEVC Lossless Encoding"; + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: return "HEVC Wavefront"; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: return "HEVC Loop Filter"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP: return "HEVC QP Values"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: return "HEVC Hierarchical Coding Type"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: return "HEVC Hierarchical Coding Layer"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP: return "HEVC Hierarchical Layer 0 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP: return "HEVC Hierarchical Layer 1 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP: return "HEVC Hierarchical Layer 2 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP: return "HEVC Hierarchical Layer 3 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP: return "HEVC Hierarchical Layer 4 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP: return "HEVC Hierarchical Layer 5 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP: return "HEVC Hierarchical Layer 6 QP"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: return "HEVC Hierarchical Lay 0 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: return "HEVC Hierarchical Lay 1 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: return "HEVC Hierarchical Lay 2 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: return "HEVC Hierarchical Lay 3 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: return "HEVC Hierarchical Lay 4 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: return "HEVC Hierarchical Lay 5 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR: return "HEVC Hierarchical Lay 6 BitRate"; + case V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB: return "HEVC General PB"; + case V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID: return "HEVC Temporal ID"; + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: return "HEVC Strong Intra Smoothing"; + case V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT: return "HEVC Intra PU Split"; + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: return "HEVC TMV Prediction"; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: return "HEVC Max Num of Candidate MVs"; + case V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE: return "HEVC ENC Without Startcode"; + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: return "HEVC Num of I-Frame b/w 2 IDR"; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: return "HEVC Loop Filter Beta Offset"; + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: return "HEVC Loop Filter TC Offset"; + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field"; + case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame"; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR"; + /* CAMERA controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; @@ -1069,6 +1181,13 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: case V4L2_CID_DETECT_MD_MODE: + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index e2ee5f00c445..c81faea96fba 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * v4l2-dv-timings - dv-timings helper functions * * Copyright 2013 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 <linux/module.h> @@ -27,6 +14,7 @@ #include <linux/v4l2-dv-timings.h> #include <media/v4l2-dv-timings.h> #include <linux/math64.h> +#include <linux/hdmi.h> MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions"); @@ -814,3 +802,143 @@ struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) return aspect; } EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); + +/** v4l2_hdmi_rx_colorimetry - determine HDMI colorimetry information + * based on various InfoFrames. + * @avi: the AVI InfoFrame + * @hdmi: the HDMI Vendor InfoFrame, may be NULL + * @height: the frame height + * + * Determines the HDMI colorimetry information, i.e. how the HDMI + * pixel color data should be interpreted. + * + * Note that some of the newer features (DCI-P3, HDR) are not yet + * implemented: the hdmi.h header needs to be updated to the HDMI 2.0 + * and CTA-861-G standards. + */ +struct v4l2_hdmi_colorimetry +v4l2_hdmi_rx_colorimetry(const struct hdmi_avi_infoframe *avi, + const struct hdmi_vendor_infoframe *hdmi, + unsigned int height) +{ + struct v4l2_hdmi_colorimetry c = { + V4L2_COLORSPACE_SRGB, + V4L2_YCBCR_ENC_DEFAULT, + V4L2_QUANTIZATION_FULL_RANGE, + V4L2_XFER_FUNC_SRGB + }; + bool is_ce = avi->video_code || (hdmi && hdmi->vic); + bool is_sdtv = height <= 576; + bool default_is_lim_range_rgb = avi->video_code > 1; + + switch (avi->colorspace) { + case HDMI_COLORSPACE_RGB: + /* RGB pixel encoding */ + switch (avi->colorimetry) { + case HDMI_COLORIMETRY_EXTENDED: + switch (avi->extended_colorimetry) { + case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB: + c.colorspace = V4L2_COLORSPACE_ADOBERGB; + c.xfer_func = V4L2_XFER_FUNC_ADOBERGB; + break; + case HDMI_EXTENDED_COLORIMETRY_BT2020: + c.colorspace = V4L2_COLORSPACE_BT2020; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + default: + break; + } + break; + default: + break; + } + switch (avi->quantization_range) { + case HDMI_QUANTIZATION_RANGE_LIMITED: + c.quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + case HDMI_QUANTIZATION_RANGE_FULL: + break; + default: + if (default_is_lim_range_rgb) + c.quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + } + break; + + default: + /* YCbCr pixel encoding */ + c.quantization = V4L2_QUANTIZATION_LIM_RANGE; + switch (avi->colorimetry) { + case HDMI_COLORIMETRY_NONE: + if (!is_ce) + break; + if (is_sdtv) { + c.colorspace = V4L2_COLORSPACE_SMPTE170M; + c.ycbcr_enc = V4L2_YCBCR_ENC_601; + } else { + c.colorspace = V4L2_COLORSPACE_REC709; + c.ycbcr_enc = V4L2_YCBCR_ENC_709; + } + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_COLORIMETRY_ITU_601: + c.colorspace = V4L2_COLORSPACE_SMPTE170M; + c.ycbcr_enc = V4L2_YCBCR_ENC_601; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_COLORIMETRY_ITU_709: + c.colorspace = V4L2_COLORSPACE_REC709; + c.ycbcr_enc = V4L2_YCBCR_ENC_709; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_COLORIMETRY_EXTENDED: + switch (avi->extended_colorimetry) { + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601: + c.colorspace = V4L2_COLORSPACE_REC709; + c.ycbcr_enc = V4L2_YCBCR_ENC_XV709; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709: + c.colorspace = V4L2_COLORSPACE_REC709; + c.ycbcr_enc = V4L2_YCBCR_ENC_XV601; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_EXTENDED_COLORIMETRY_S_YCC_601: + c.colorspace = V4L2_COLORSPACE_SRGB; + c.ycbcr_enc = V4L2_YCBCR_ENC_601; + c.xfer_func = V4L2_XFER_FUNC_SRGB; + break; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601: + c.colorspace = V4L2_COLORSPACE_ADOBERGB; + c.ycbcr_enc = V4L2_YCBCR_ENC_601; + c.xfer_func = V4L2_XFER_FUNC_ADOBERGB; + break; + case HDMI_EXTENDED_COLORIMETRY_BT2020: + c.colorspace = V4L2_COLORSPACE_BT2020; + c.ycbcr_enc = V4L2_YCBCR_ENC_BT2020; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM: + c.colorspace = V4L2_COLORSPACE_BT2020; + c.ycbcr_enc = V4L2_YCBCR_ENC_BT2020_CONST_LUM; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + default: /* fall back to ITU_709 */ + c.colorspace = V4L2_COLORSPACE_REC709; + c.ycbcr_enc = V4L2_YCBCR_ENC_709; + c.xfer_func = V4L2_XFER_FUNC_709; + break; + } + break; + default: + break; + } + /* + * YCC Quantization Range signaling is more-or-less broken, + * let's just ignore this. + */ + break; + } + return c; +} +EXPORT_SYMBOL_GPL(v4l2_hdmi_rx_colorimetry); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 260288ca4f55..f48c505550e0 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1273,6 +1273,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_VC1_ANNEX_L: descr = "VC-1 (SMPTE 412M Annex L)"; break; case V4L2_PIX_FMT_VP8: descr = "VP8"; break; case V4L2_PIX_FMT_VP9: descr = "VP9"; break; + case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break; case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break; @@ -2611,7 +2612,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY), - IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), + IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), @@ -2832,14 +2833,15 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, size_t array_size = 0; void __user *user_ptr = NULL; void **kernel_ptr = NULL; + const size_t ioc_size = _IOC_SIZE(cmd); /* Copy arguments into temp kernel buffer */ if (_IOC_DIR(cmd) != _IOC_NONE) { - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + if (ioc_size <= sizeof(sbuf)) { parg = sbuf; } else { /* too big to allocate from stack */ - mbuf = kvmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + mbuf = kvmalloc(ioc_size, GFP_KERNEL); if (NULL == mbuf) return -ENOMEM; parg = mbuf; @@ -2847,7 +2849,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; if (_IOC_DIR(cmd) & _IOC_WRITE) { - unsigned int n = _IOC_SIZE(cmd); + unsigned int n = ioc_size; /* * In some cases, only a few fields are used as input, @@ -2868,11 +2870,11 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, goto out; /* zero out anything we don't copy from userspace */ - if (n < _IOC_SIZE(cmd)) - memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); + if (n < ioc_size) + memset((u8 *)parg + n, 0, ioc_size - n); } else { /* read-only ioctl */ - memset(parg, 0, _IOC_SIZE(cmd)); + memset(parg, 0, ioc_size); } } @@ -2930,7 +2932,7 @@ out_array_args: switch (_IOC_DIR(cmd)) { case _IOC_READ: case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + if (copy_to_user((void __user *)arg, parg, ioc_size)) err = -EFAULT; break; } diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 1d550afeda13..0fc185a2ce90 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Media Controller ancillary functions * @@ -5,16 +7,6 @@ * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com> * Copyright (C) 2006-2010 Nokia Corporation * Copyright (c) 2016 Intel Corporation. - * - * 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 <linux/module.h> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index c5639817db34..f9eed938d348 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -187,27 +187,51 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) switch (cmd) { case VIDIOC_QUERYCTRL: + /* + * TODO: this really should be folded into v4l2_queryctrl (this + * currently returns -EINVAL for NULL control handlers). + * However, v4l2_queryctrl() is still called directly by + * drivers as well and until that has been addressed I believe + * it is safer to do the check here. The same is true for the + * other control ioctls below. + */ + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_queryctrl(vfh->ctrl_handler, arg); case VIDIOC_QUERY_EXT_CTRL: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg); case VIDIOC_QUERYMENU: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_querymenu(vfh->ctrl_handler, arg); case VIDIOC_G_CTRL: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_g_ctrl(vfh->ctrl_handler, arg); case VIDIOC_S_CTRL: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg); case VIDIOC_G_EXT_CTRLS: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg); case VIDIOC_S_EXT_CTRLS: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg); case VIDIOC_TRY_EXT_CTRLS: + if (!vfh->ctrl_handler) + return -ENOTTY; return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg); case VIDIOC_DQEVENT: @@ -239,6 +263,19 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return -EPERM; return v4l2_subdev_call(sd, core, s_register, p); } + case VIDIOC_DBG_G_CHIP_INFO: + { + struct v4l2_dbg_chip_info *p = arg; + + if (p->match.type != V4L2_CHIP_MATCH_SUBDEV || p->match.addr) + return -EINVAL; + if (sd->ops->core && sd->ops->core->s_register) + p->flags |= V4L2_CHIP_FL_WRITABLE; + if (sd->ops->core && sd->ops->core->g_register) + p->flags |= V4L2_CHIP_FL_READABLE; + strlcpy(p->name, sd->name, sizeof(p->name)); + return 0; + } #endif case VIDIOC_LOG_STATUS: { @@ -260,6 +297,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; + memset(format->reserved, 0, sizeof(format->reserved)); + memset(format->format.reserved, 0, sizeof(format->format.reserved)); return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format); } @@ -270,6 +309,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; + memset(format->reserved, 0, sizeof(format->reserved)); + memset(format->format.reserved, 0, sizeof(format->format.reserved)); return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format); } @@ -281,6 +322,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; + memset(crop->reserved, 0, sizeof(crop->reserved)); memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; @@ -298,6 +340,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; + memset(crop->reserved, 0, sizeof(crop->reserved)); rval = check_crop(sd, crop); if (rval) return rval; @@ -326,6 +369,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (code->pad >= sd->entity.num_pads) return -EINVAL; + memset(code->reserved, 0, sizeof(code->reserved)); return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad, code); } @@ -340,6 +384,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fse->pad >= sd->entity.num_pads) return -EINVAL; + memset(fse->reserved, 0, sizeof(fse->reserved)); return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad, fse); } @@ -350,6 +395,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fi->pad >= sd->entity.num_pads) return -EINVAL; + memset(fi->reserved, 0, sizeof(fi->reserved)); return v4l2_subdev_call(sd, video, g_frame_interval, arg); } @@ -359,6 +405,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fi->pad >= sd->entity.num_pads) return -EINVAL; + memset(fi->reserved, 0, sizeof(fi->reserved)); return v4l2_subdev_call(sd, video, s_frame_interval, arg); } @@ -372,6 +419,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (fie->pad >= sd->entity.num_pads) return -EINVAL; + memset(fie->reserved, 0, sizeof(fie->reserved)); return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad, fie); } @@ -383,6 +431,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; + memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( sd, pad, get_selection, subdev_fh->pad, sel); } @@ -394,6 +443,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; + memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( sd, pad, set_selection, subdev_fh->pad, sel); } diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index f412429cf5ba..add2edb23eac 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -244,9 +244,8 @@ static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, goto out_free_pages; } - dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)dma->vaddr, - nr_pages << PAGE_SHIFT); + dprintk(1, "vmalloc is at addr %p, size=%d\n", + dma->vaddr, nr_pages << PAGE_SHIFT); memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; |