1
0

Merge tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:

 - new CEC pin injection code for testing purposes

 - DVB frontend cxd2099 promoted from staging

 - new platform driver for Sony cxd2880 DVB devices

 - new sensor drivers: mt9t112, ov2685, ov5695, ov772x, tda1997x,
   tw9910.c

 - removal of unused cx18 and ivtv alsa mixers

 - the reneseas-ceu driver doesn't depend on soc_camera anymore and
   moved from staging

 - removed the mantis_vp3028 driver, unused since 2009

 - s5p-mfc: add support for version 10 of the MSP

 - added a decoder for imon protocol

 - atomisp: lots of cleanups

 - imx074 and mt9t031: don't depend on soc_camera anymore, being
   promoted from staging

 - added helper functions to better support DVB I2C binding

 - lots of driver improvements and cleanups

* tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (438 commits)
  media: v4l2-ioctl: rename a temp var that stores _IOC_SIZE(cmd)
  media: fimc-capture: get rid of two warnings
  media: dvb-usb-v2: fix a missing dependency of I2C_MUX
  media: uvc: to the right check at uvc_ioctl_enum_framesizes()
  media: cec-core: fix a bug at cec_error_inj_write()
  media: tda9840: cleanup a warning
  media: tm6000:  avoid casting just to print pointer address
  media: em28xx-input: improve error handling code
  media: zr364xx: avoid casting just to print pointer address
  media: vivid-radio-rx: add a cast to avoid a warning
  media: saa7134-alsa: don't use casts to print a buffer address
  media: solo6x10: get rid of an address space warning
  media: zoran: don't cast pointers to print them
  media: ir-kbd-i2c: change the if logic to avoid a warning
  media: ir-kbd-i2c: improve error handling code
  media: saa7134-input: improve error handling
  media: s2255drv: fix a casting warning
  media: ivtvfb: Cleanup some warnings
  media: videobuf-dma-sg: Fix a weird cast
  soc_camera: fix a weird cast on printk
  ...
This commit is contained in:
Linus Torvalds
2018-04-03 17:16:59 -07:00
572 changed files with 37812 additions and 21347 deletions

View File

@@ -141,6 +141,7 @@ config DVB_CORE
tristate
depends on MEDIA_SUPPORT
depends on MEDIA_DIGITAL_TV_SUPPORT
depends on (I2C || I2C=n)
default y
select CRC32

View File

@@ -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.

View File

@@ -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

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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>

View File

@@ -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:

View File

@@ -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);

View File

@@ -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);

View File

@@ -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:

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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/

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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, &regval);
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");

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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, &reg_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, &reg_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, &reg_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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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--;
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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)

View File

@@ -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:

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -48,6 +48,7 @@ struct si2168_dev {
u8 ts_mode;
bool ts_clock_inv;
bool ts_clock_gapped;
bool spectral_inversion;
};
/* firmware command struct */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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) \

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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.
*/
/*

View File

@@ -35,96 +35,28 @@
* Register manipulation
*/
#define ADV748X_REGMAP_CONF(n) \
{ \
.name = n, \
.reg_bits = 8, \
.val_bits = 8, \
.max_register = 0xff, \
.cache_type = REGCACHE_NONE, \
}
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,
.max_register = 0xff,
.cache_type = REGCACHE_NONE,
},
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];
return i2c_new_dummy(client->adapter, io_read(state, io_reg) >> 1);
io_write(state, io_reg, client->addr << 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 */

View File

@@ -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)

View File

@@ -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,

View File

@@ -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.
*/

View File

@@ -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 (addr)
io_write(sd, io_reg, addr << 1);
return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
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);
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;
}

View File

@@ -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.
*
*/
/*

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

1140
drivers/media/i2c/mt9t112.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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 = {

View File

@@ -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;
}

846
drivers/media/i2c/ov2685.c Normal file
View File

@@ -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 *)&reg_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");

Some files were not shown because too many files have changed in this diff Show More