|
@@ -1357,9 +1357,9 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
|
|
|
static void m88ds3103_release(struct dvb_frontend *fe)
|
|
|
{
|
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
|
+ struct i2c_client *client = priv->client;
|
|
|
|
|
|
- i2c_del_mux_adapter(priv->i2c_adapter);
|
|
|
- kfree(priv);
|
|
|
+ i2c_unregister_device(client);
|
|
|
}
|
|
|
|
|
|
static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
|
@@ -1401,43 +1401,158 @@ static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * XXX: That is wrapper to m88ds3103_probe() via driver core in order to provide
|
|
|
+ * proper I2C client for legacy media attach binding.
|
|
|
+ * New users must use I2C client binding directly!
|
|
|
+ */
|
|
|
struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
|
|
|
{
|
|
|
+ struct i2c_client *client;
|
|
|
+ struct i2c_board_info board_info;
|
|
|
+ struct m88ds3103_platform_data pdata;
|
|
|
+
|
|
|
+ pdata.clk = cfg->clock;
|
|
|
+ pdata.i2c_wr_max = cfg->i2c_wr_max;
|
|
|
+ pdata.ts_mode = cfg->ts_mode;
|
|
|
+ pdata.ts_clk = cfg->ts_clk;
|
|
|
+ pdata.ts_clk_pol = cfg->ts_clk_pol;
|
|
|
+ pdata.spec_inv = cfg->spec_inv;
|
|
|
+ pdata.agc = cfg->agc;
|
|
|
+ pdata.agc_inv = cfg->agc_inv;
|
|
|
+ pdata.clk_out = cfg->clock_out;
|
|
|
+ pdata.envelope_mode = cfg->envelope_mode;
|
|
|
+ pdata.lnb_hv_pol = cfg->lnb_hv_pol;
|
|
|
+ pdata.lnb_en_pol = cfg->lnb_en_pol;
|
|
|
+ pdata.attach_in_use = true;
|
|
|
+
|
|
|
+ memset(&board_info, 0, sizeof(board_info));
|
|
|
+ strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE);
|
|
|
+ board_info.addr = cfg->i2c_addr;
|
|
|
+ board_info.platform_data = &pdata;
|
|
|
+ client = i2c_new_device(i2c, &board_info);
|
|
|
+ if (!client || !client->dev.driver)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ *tuner_i2c_adapter = pdata.get_i2c_adapter(client);
|
|
|
+ return pdata.get_dvb_frontend(client);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(m88ds3103_attach);
|
|
|
+
|
|
|
+static struct dvb_frontend_ops m88ds3103_ops = {
|
|
|
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
|
|
|
+ .info = {
|
|
|
+ .name = "Montage M88DS3103",
|
|
|
+ .frequency_min = 950000,
|
|
|
+ .frequency_max = 2150000,
|
|
|
+ .frequency_tolerance = 5000,
|
|
|
+ .symbol_rate_min = 1000000,
|
|
|
+ .symbol_rate_max = 45000000,
|
|
|
+ .caps = FE_CAN_INVERSION_AUTO |
|
|
|
+ FE_CAN_FEC_1_2 |
|
|
|
+ FE_CAN_FEC_2_3 |
|
|
|
+ FE_CAN_FEC_3_4 |
|
|
|
+ FE_CAN_FEC_4_5 |
|
|
|
+ FE_CAN_FEC_5_6 |
|
|
|
+ FE_CAN_FEC_6_7 |
|
|
|
+ FE_CAN_FEC_7_8 |
|
|
|
+ FE_CAN_FEC_8_9 |
|
|
|
+ FE_CAN_FEC_AUTO |
|
|
|
+ FE_CAN_QPSK |
|
|
|
+ FE_CAN_RECOVER |
|
|
|
+ FE_CAN_2G_MODULATION
|
|
|
+ },
|
|
|
+
|
|
|
+ .release = m88ds3103_release,
|
|
|
+
|
|
|
+ .get_tune_settings = m88ds3103_get_tune_settings,
|
|
|
+
|
|
|
+ .init = m88ds3103_init,
|
|
|
+ .sleep = m88ds3103_sleep,
|
|
|
+
|
|
|
+ .set_frontend = m88ds3103_set_frontend,
|
|
|
+ .get_frontend = m88ds3103_get_frontend,
|
|
|
+
|
|
|
+ .read_status = m88ds3103_read_status,
|
|
|
+ .read_snr = m88ds3103_read_snr,
|
|
|
+ .read_ber = m88ds3103_read_ber,
|
|
|
+
|
|
|
+ .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
|
|
|
+ .diseqc_send_burst = m88ds3103_diseqc_send_burst,
|
|
|
+
|
|
|
+ .set_tone = m88ds3103_set_tone,
|
|
|
+ .set_voltage = m88ds3103_set_voltage,
|
|
|
+};
|
|
|
+
|
|
|
+static struct dvb_frontend *m88ds3103_get_dvb_frontend(struct i2c_client *client)
|
|
|
+{
|
|
|
+ struct m88ds3103_priv *dev = i2c_get_clientdata(client);
|
|
|
+
|
|
|
+ dev_dbg(&client->dev, "\n");
|
|
|
+
|
|
|
+ return &dev->fe;
|
|
|
+}
|
|
|
+
|
|
|
+static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client)
|
|
|
+{
|
|
|
+ struct m88ds3103_priv *dev = i2c_get_clientdata(client);
|
|
|
+
|
|
|
+ dev_dbg(&client->dev, "\n");
|
|
|
+
|
|
|
+ return dev->i2c_adapter;
|
|
|
+}
|
|
|
+
|
|
|
+static int m88ds3103_probe(struct i2c_client *client,
|
|
|
+ const struct i2c_device_id *id)
|
|
|
+{
|
|
|
+ struct m88ds3103_priv *dev;
|
|
|
+ struct m88ds3103_platform_data *pdata = client->dev.platform_data;
|
|
|
int ret;
|
|
|
- struct m88ds3103_priv *priv;
|
|
|
u8 chip_id, u8tmp;
|
|
|
|
|
|
- /* allocate memory for the internal priv */
|
|
|
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
|
- if (!priv) {
|
|
|
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
|
+ if (!dev) {
|
|
|
ret = -ENOMEM;
|
|
|
- dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
- priv->cfg = cfg;
|
|
|
- priv->i2c = i2c;
|
|
|
- mutex_init(&priv->i2c_mutex);
|
|
|
+ dev->client = client;
|
|
|
+ dev->i2c = client->adapter;
|
|
|
+ dev->config.i2c_addr = client->addr;
|
|
|
+ dev->config.clock = pdata->clk;
|
|
|
+ dev->config.i2c_wr_max = pdata->i2c_wr_max;
|
|
|
+ dev->config.ts_mode = pdata->ts_mode;
|
|
|
+ dev->config.ts_clk = pdata->ts_clk;
|
|
|
+ dev->config.ts_clk_pol = pdata->ts_clk_pol;
|
|
|
+ dev->config.spec_inv = pdata->spec_inv;
|
|
|
+ dev->config.agc_inv = pdata->agc_inv;
|
|
|
+ dev->config.clock_out = pdata->clk_out;
|
|
|
+ dev->config.envelope_mode = pdata->envelope_mode;
|
|
|
+ dev->config.agc = pdata->agc;
|
|
|
+ dev->config.lnb_hv_pol = pdata->lnb_hv_pol;
|
|
|
+ dev->config.lnb_en_pol = pdata->lnb_en_pol;
|
|
|
+ dev->cfg = &dev->config;
|
|
|
+ mutex_init(&dev->i2c_mutex);
|
|
|
|
|
|
/* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */
|
|
|
- ret = m88ds3103_rd_reg(priv, 0x00, &chip_id);
|
|
|
+ ret = m88ds3103_rd_reg(dev, 0x00, &chip_id);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
+ goto err_kfree;
|
|
|
|
|
|
chip_id >>= 1;
|
|
|
- dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
|
|
|
+ dev_dbg(&client->dev, "chip_id=%02x\n", chip_id);
|
|
|
|
|
|
switch (chip_id) {
|
|
|
case M88RS6000_CHIP_ID:
|
|
|
case M88DS3103_CHIP_ID:
|
|
|
break;
|
|
|
default:
|
|
|
- goto err;
|
|
|
+ goto err_kfree;
|
|
|
}
|
|
|
- priv->chip_id = chip_id;
|
|
|
+ dev->chip_id = chip_id;
|
|
|
|
|
|
- switch (priv->cfg->clock_out) {
|
|
|
+ switch (dev->cfg->clock_out) {
|
|
|
case M88DS3103_CLOCK_OUT_DISABLED:
|
|
|
u8tmp = 0x80;
|
|
|
break;
|
|
@@ -1448,7 +1563,7 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
u8tmp = 0x10;
|
|
|
break;
|
|
|
default:
|
|
|
- goto err;
|
|
|
+ goto err_kfree;
|
|
|
}
|
|
|
|
|
|
/* 0x29 register is defined differently for m88rs6000. */
|
|
@@ -1456,91 +1571,80 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
|
|
|
if (chip_id == M88RS6000_CHIP_ID)
|
|
|
u8tmp = 0x00;
|
|
|
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
|
|
|
+ ret = m88ds3103_wr_reg(dev, 0x29, u8tmp);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
+ goto err_kfree;
|
|
|
|
|
|
/* sleep */
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
|
|
|
+ ret = m88ds3103_wr_reg_mask(dev, 0x08, 0x00, 0x01);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
|
|
|
+ goto err_kfree;
|
|
|
+ ret = m88ds3103_wr_reg_mask(dev, 0x04, 0x01, 0x01);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
-
|
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
|
|
|
+ goto err_kfree;
|
|
|
+ ret = m88ds3103_wr_reg_mask(dev, 0x23, 0x10, 0x10);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
+ goto err_kfree;
|
|
|
|
|
|
/* create mux i2c adapter for tuner */
|
|
|
- priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
|
|
|
- m88ds3103_select, m88ds3103_deselect);
|
|
|
- if (priv->i2c_adapter == NULL)
|
|
|
- goto err;
|
|
|
-
|
|
|
- *tuner_i2c_adapter = priv->i2c_adapter;
|
|
|
+ dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
|
|
|
+ dev, 0, 0, 0, m88ds3103_select,
|
|
|
+ m88ds3103_deselect);
|
|
|
+ if (dev->i2c_adapter == NULL)
|
|
|
+ goto err_kfree;
|
|
|
|
|
|
/* create dvb_frontend */
|
|
|
- memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
|
|
- if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
|
- strncpy(priv->fe.ops.info.name,
|
|
|
- "Montage M88RS6000", sizeof(priv->fe.ops.info.name));
|
|
|
- priv->fe.demodulator_priv = priv;
|
|
|
-
|
|
|
- return &priv->fe;
|
|
|
+ memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
|
|
+ if (dev->chip_id == M88RS6000_CHIP_ID)
|
|
|
+ strncpy(dev->fe.ops.info.name,
|
|
|
+ "Montage M88RS6000", sizeof(dev->fe.ops.info.name));
|
|
|
+ if (!pdata->attach_in_use)
|
|
|
+ dev->fe.ops.release = NULL;
|
|
|
+ dev->fe.demodulator_priv = dev;
|
|
|
+ i2c_set_clientdata(client, dev);
|
|
|
+
|
|
|
+ /* setup callbacks */
|
|
|
+ pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
|
|
|
+ pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
|
|
|
+ return 0;
|
|
|
+err_kfree:
|
|
|
+ kfree(dev);
|
|
|
err:
|
|
|
- dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
|
|
|
- kfree(priv);
|
|
|
- return NULL;
|
|
|
+ dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
|
+ return ret;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(m88ds3103_attach);
|
|
|
-
|
|
|
-static struct dvb_frontend_ops m88ds3103_ops = {
|
|
|
- .delsys = { SYS_DVBS, SYS_DVBS2 },
|
|
|
- .info = {
|
|
|
- .name = "Montage M88DS3103",
|
|
|
- .frequency_min = 950000,
|
|
|
- .frequency_max = 2150000,
|
|
|
- .frequency_tolerance = 5000,
|
|
|
- .symbol_rate_min = 1000000,
|
|
|
- .symbol_rate_max = 45000000,
|
|
|
- .caps = FE_CAN_INVERSION_AUTO |
|
|
|
- FE_CAN_FEC_1_2 |
|
|
|
- FE_CAN_FEC_2_3 |
|
|
|
- FE_CAN_FEC_3_4 |
|
|
|
- FE_CAN_FEC_4_5 |
|
|
|
- FE_CAN_FEC_5_6 |
|
|
|
- FE_CAN_FEC_6_7 |
|
|
|
- FE_CAN_FEC_7_8 |
|
|
|
- FE_CAN_FEC_8_9 |
|
|
|
- FE_CAN_FEC_AUTO |
|
|
|
- FE_CAN_QPSK |
|
|
|
- FE_CAN_RECOVER |
|
|
|
- FE_CAN_2G_MODULATION
|
|
|
- },
|
|
|
|
|
|
- .release = m88ds3103_release,
|
|
|
+static int m88ds3103_remove(struct i2c_client *client)
|
|
|
+{
|
|
|
+ struct m88ds3103_priv *dev = i2c_get_clientdata(client);
|
|
|
|
|
|
- .get_tune_settings = m88ds3103_get_tune_settings,
|
|
|
+ dev_dbg(&client->dev, "\n");
|
|
|
|
|
|
- .init = m88ds3103_init,
|
|
|
- .sleep = m88ds3103_sleep,
|
|
|
+ i2c_del_mux_adapter(dev->i2c_adapter);
|
|
|
|
|
|
- .set_frontend = m88ds3103_set_frontend,
|
|
|
- .get_frontend = m88ds3103_get_frontend,
|
|
|
-
|
|
|
- .read_status = m88ds3103_read_status,
|
|
|
- .read_snr = m88ds3103_read_snr,
|
|
|
- .read_ber = m88ds3103_read_ber,
|
|
|
+ kfree(dev);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
|
|
|
- .diseqc_send_burst = m88ds3103_diseqc_send_burst,
|
|
|
+static const struct i2c_device_id m88ds3103_id_table[] = {
|
|
|
+ {"m88ds3103", 0},
|
|
|
+ {}
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);
|
|
|
|
|
|
- .set_tone = m88ds3103_set_tone,
|
|
|
- .set_voltage = m88ds3103_set_voltage,
|
|
|
+static struct i2c_driver m88ds3103_driver = {
|
|
|
+ .driver = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .name = "m88ds3103",
|
|
|
+ .suppress_bind_attrs = true,
|
|
|
+ },
|
|
|
+ .probe = m88ds3103_probe,
|
|
|
+ .remove = m88ds3103_remove,
|
|
|
+ .id_table = m88ds3103_id_table,
|
|
|
};
|
|
|
|
|
|
+module_i2c_driver(m88ds3103_driver);
|
|
|
+
|
|
|
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
|
MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
|
|
|
MODULE_LICENSE("GPL");
|