[media] lgdt3306a: support i2c mux for use by em28xx
Adds an i2c mux to the lgdt3306a demodulator. This was done to support the Hauppauge WinTV-dualHD 01595 USB TV tuner (em28xx), which utilizes two si2157 tuners behind gate control. Signed-off-by: Kevin Cheng <kcheng@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
parent
3f70608872
commit
4f75189024
3 changed files with 113 additions and 1 deletions
|
@ -619,7 +619,7 @@ config DVB_LGDT3305
|
||||||
|
|
||||||
config DVB_LGDT3306A
|
config DVB_LGDT3306A
|
||||||
tristate "LG Electronics LGDT3306A based"
|
tristate "LG Electronics LGDT3306A based"
|
||||||
depends on DVB_CORE && I2C
|
depends on DVB_CORE && I2C && I2C_MUX
|
||||||
default m if !MEDIA_SUBDRV_AUTOSELECT
|
default m if !MEDIA_SUBDRV_AUTOSELECT
|
||||||
help
|
help
|
||||||
An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want
|
An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/dvb/frontend.h>
|
#include <linux/dvb/frontend.h>
|
||||||
#include "dvb_math.h"
|
#include "dvb_math.h"
|
||||||
#include "lgdt3306a.h"
|
#include "lgdt3306a.h"
|
||||||
|
#include <linux/i2c-mux.h>
|
||||||
|
|
||||||
|
|
||||||
static int debug;
|
static int debug;
|
||||||
|
@ -65,6 +66,8 @@ struct lgdt3306a_state {
|
||||||
enum fe_modulation current_modulation;
|
enum fe_modulation current_modulation;
|
||||||
u32 current_frequency;
|
u32 current_frequency;
|
||||||
u32 snr;
|
u32 snr;
|
||||||
|
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2131,6 +2134,111 @@ static const struct dvb_frontend_ops lgdt3306a_ops = {
|
||||||
.search = lgdt3306a_search,
|
.search = lgdt3306a_search,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int lgdt3306a_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = i2c_mux_priv(muxc);
|
||||||
|
struct lgdt3306a_state *state = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
return lgdt3306a_i2c_gate_ctrl(&state->frontend, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = i2c_mux_priv(muxc);
|
||||||
|
struct lgdt3306a_state *state = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lgdt3306a_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct lgdt3306a_config *config;
|
||||||
|
struct lgdt3306a_state *state;
|
||||||
|
struct dvb_frontend *fe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
config = kzalloc(sizeof(struct lgdt3306a_config), GFP_KERNEL);
|
||||||
|
if (config == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(config, client->dev.platform_data,
|
||||||
|
sizeof(struct lgdt3306a_config));
|
||||||
|
|
||||||
|
config->i2c_addr = client->addr;
|
||||||
|
fe = lgdt3306a_attach(config, client->adapter);
|
||||||
|
if (fe == NULL) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto err_fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, fe->demodulator_priv);
|
||||||
|
state = fe->demodulator_priv;
|
||||||
|
|
||||||
|
/* create mux i2c adapter for tuner */
|
||||||
|
state->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||||
|
1, 0, I2C_MUX_LOCKED,
|
||||||
|
lgdt3306a_select, lgdt3306a_deselect);
|
||||||
|
if (!state->muxc) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_kfree;
|
||||||
|
}
|
||||||
|
state->muxc->priv = client;
|
||||||
|
ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_kfree;
|
||||||
|
|
||||||
|
/* create dvb_frontend */
|
||||||
|
fe->ops.i2c_gate_ctrl = NULL;
|
||||||
|
*config->i2c_adapter = state->muxc->adapter[0];
|
||||||
|
*config->fe = fe;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_kfree:
|
||||||
|
kfree(state);
|
||||||
|
err_fe:
|
||||||
|
kfree(config);
|
||||||
|
fail:
|
||||||
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lgdt3306a_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct lgdt3306a_state *state = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
i2c_mux_del_adapters(state->muxc);
|
||||||
|
|
||||||
|
state->frontend.ops.release = NULL;
|
||||||
|
state->frontend.demodulator_priv = NULL;
|
||||||
|
|
||||||
|
kfree(state->cfg);
|
||||||
|
kfree(state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id lgdt3306a_id_table[] = {
|
||||||
|
{"lgdt3306a", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table);
|
||||||
|
|
||||||
|
static struct i2c_driver lgdt3306a_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lgdt3306a",
|
||||||
|
.suppress_bind_attrs = true,
|
||||||
|
},
|
||||||
|
.probe = lgdt3306a_probe,
|
||||||
|
.remove = lgdt3306a_remove,
|
||||||
|
.id_table = lgdt3306a_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(lgdt3306a_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver");
|
MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver");
|
||||||
MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>");
|
MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -56,6 +56,10 @@ struct lgdt3306a_config {
|
||||||
|
|
||||||
/* demod clock freq in MHz; 24 or 25 supported */
|
/* demod clock freq in MHz; 24 or 25 supported */
|
||||||
int xtalMHz;
|
int xtalMHz;
|
||||||
|
|
||||||
|
/* returned by driver if using i2c bus multiplexing */
|
||||||
|
struct dvb_frontend **fe;
|
||||||
|
struct i2c_adapter **i2c_adapter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if IS_REACHABLE(CONFIG_DVB_LGDT3306A)
|
#if IS_REACHABLE(CONFIG_DVB_LGDT3306A)
|
||||||
|
|
Loading…
Reference in a new issue