diff options
Diffstat (limited to 'drivers/media/dvb-frontends/m88ds3103.c')
-rw-r--r-- | drivers/media/dvb-frontends/m88ds3103.c | 268 |
1 files changed, 186 insertions, 82 deletions
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index e45641f2c794..01b9dedff3d1 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -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"); |