diff options
Diffstat (limited to 'drivers/media/dvb-frontends/stv6110x.c')
| -rw-r--r-- | drivers/media/dvb-frontends/stv6110x.c | 181 |
1 files changed, 134 insertions, 47 deletions
diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index f36cab12bdc7..33c8105da1c3 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* STV6110(A) Silicon tuner driver @@ -5,19 +6,6 @@ Copyright (C) ST Microelectronics - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/init.h> @@ -26,12 +14,15 @@ #include <linux/slab.h> #include <linux/string.h> -#include "dvb_frontend.h" +#include <media/dvb_frontend.h> #include "stv6110x_reg.h" #include "stv6110x.h" #include "stv6110x_priv.h" +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + static unsigned int verbose; module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose, "Set Verbosity level"); @@ -43,7 +34,7 @@ static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) u8 b0[] = { reg }; u8 b1[] = { 0 }; struct i2c_msg msg[] = { - { .addr = config->addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->addr, .flags = 0, .buf = b0, .len = 1 }, { .addr = config->addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; @@ -61,7 +52,8 @@ static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 da { int ret; const struct stv6110x_config *config = stv6110x->config; - u8 buf[len + 1]; + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg msg = { .addr = config->addr, .flags = 0, @@ -69,6 +61,13 @@ static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 da .len = len + 1 }; + if (1 + len > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr: len=%d is too big!\n", + KBUILD_MODNAME, len); + return -EINVAL; + } + if (start + len > 8) return -EINVAL; @@ -86,7 +85,9 @@ static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 da static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) { - return stv6110x_write_regs(stv6110x, reg, &data, 1); + u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return stv6110x_write_regs(stv6110x, reg, &tmp, 1); } static int stv6110x_init(struct dvb_frontend *fe) @@ -324,22 +325,54 @@ static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status) } -static int stv6110x_release(struct dvb_frontend *fe) +static void stv6110x_release(struct dvb_frontend *fe) { struct stv6110x_state *stv6110x = fe->tuner_priv; fe->tuner_priv = NULL; kfree(stv6110x); +} - return 0; +static void st6110x_init_regs(struct stv6110x_state *stv6110x) +{ + u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; + + memcpy(stv6110x->regs, default_regs, 8); +} + +static void stv6110x_setup_divider(struct stv6110x_state *stv6110x) +{ + switch (stv6110x->config->clk_div) { + default: + case 1: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 0); + break; + case 2: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 1); + break; + case 4: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 2); + break; + case 8: + case 0: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 3); + break; + } } -static struct dvb_tuner_ops stv6110x_ops = { +static const struct dvb_tuner_ops stv6110x_ops = { .info = { - .name = "STV6110(A) Silicon Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0, + .name = "STV6110(A) Silicon Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .release = stv6110x_release }; @@ -358,47 +391,101 @@ static struct stv6110x_devctl stv6110x_ctl = { .tuner_get_status = stv6110x_get_status, }; -struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, +static void stv6110x_set_frontend_opts(struct stv6110x_state *stv6110x) +{ + stv6110x->frontend->tuner_priv = stv6110x; + stv6110x->frontend->ops.tuner_ops = stv6110x_ops; +} + +static struct stv6110x_devctl *stv6110x_get_devctl(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return stv6110x->devctl; +} + +static int stv6110x_probe(struct i2c_client *client) +{ + struct stv6110x_config *config = client->dev.platform_data; + + struct stv6110x_state *stv6110x; + + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); + if (!stv6110x) + return -ENOMEM; + + stv6110x->frontend = config->frontend; + stv6110x->i2c = client->adapter; + stv6110x->config = config; + stv6110x->devctl = &stv6110x_ctl; + + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); + + dev_info(&stv6110x->i2c->dev, "Probed STV6110x\n"); + + i2c_set_clientdata(client, stv6110x); + + /* setup callbacks */ + config->get_devctl = stv6110x_get_devctl; + + return 0; +} + +static void stv6110x_remove(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + stv6110x_release(stv6110x->frontend); +} + +const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, const struct stv6110x_config *config, struct i2c_adapter *i2c) { struct stv6110x_state *stv6110x; - u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; - stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); if (!stv6110x) return NULL; + stv6110x->frontend = fe; stv6110x->i2c = i2c; stv6110x->config = config; stv6110x->devctl = &stv6110x_ctl; - memcpy(stv6110x->regs, default_regs, 8); - /* setup divider */ - switch (stv6110x->config->clk_div) { - default: - case 1: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); - break; - case 2: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); - break; - case 4: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); - break; - case 8: - case 0: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); - break; - } + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); fe->tuner_priv = stv6110x; fe->ops.tuner_ops = stv6110x_ops; - printk(KERN_INFO "%s: Attaching STV6110x\n", __func__); + dev_info(&stv6110x->i2c->dev, "Attaching STV6110x\n"); return stv6110x->devctl; } -EXPORT_SYMBOL(stv6110x_attach); +EXPORT_SYMBOL_GPL(stv6110x_attach); + +static const struct i2c_device_id stv6110x_id_table[] = { + { "stv6110x" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); + +static struct i2c_driver stv6110x_driver = { + .driver = { + .name = "stv6110x", + .suppress_bind_attrs = true, + }, + .probe = stv6110x_probe, + .remove = stv6110x_remove, + .id_table = stv6110x_id_table, +}; + +module_i2c_driver(stv6110x_driver); MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("STV6110x Silicon tuner"); |
