Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hauppauge WinTV-dualHD (Model 01595) Support #2091

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/media/dvb-frontends/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ config DVB_LGDT3305

config DVB_LGDT3306A
tristate "LG Electronics LGDT3306A based"
depends on DVB_CORE && I2C
depends on DVB_CORE && I2C && I2C_MUX
default m if !MEDIA_SUBDRV_AUTOSELECT
help
An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want
Expand Down
117 changes: 117 additions & 0 deletions drivers/media/dvb-frontends/lgdt3306a.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/dvb/frontend.h>
#include "dvb_math.h"
#include "lgdt3306a.h"
#include <linux/i2c-mux.h>


static int debug;
Expand Down Expand Up @@ -65,6 +66,8 @@ struct lgdt3306a_state {
enum fe_modulation current_modulation;
u32 current_frequency;
u32 snr;

struct i2c_mux_core *muxc;
};

/*
Expand Down Expand Up @@ -621,6 +624,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state,
case QAM_256:
ret = lgdt3306a_set_qam(state, QAM_256);
break;
case QAM_AUTO:
ret = lgdt3306a_set_qam(state, QAM_64);
break;
default:
return -EINVAL;
}
Expand All @@ -646,6 +652,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state,
break;
case QAM_64:
case QAM_256:
case QAM_AUTO:
break;
default:
return -EINVAL;
Expand Down Expand Up @@ -700,6 +707,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;
Expand All @@ -723,6 +731,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:
Expand Down Expand Up @@ -1641,6 +1650,9 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
case QAM_256:
ref_snr = 2800; /* 28dB */
break;
case QAM_AUTO:
ref_snr = 2200; /* 22dB */
break;
default:
return -EINVAL;
}
Expand Down Expand Up @@ -2131,6 +2143,111 @@ static struct dvb_frontend_ops lgdt3306a_ops = {
.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_AUTHOR("Fred Richter <frichter@hauppauge.com>");
MODULE_LICENSE("GPL");
Expand Down
4 changes: 4 additions & 0 deletions drivers/media/dvb-frontends/lgdt3306a.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ struct lgdt3306a_config {

/* demod clock freq in MHz; 24 or 25 supported */
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)
Expand Down
Loading