Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Antti Palosaari | 2679 | 99.78% | 1 | 20.00% |
Thomas Gleixner | 2 | 0.07% | 1 | 20.00% |
Uwe Kleine-König | 2 | 0.07% | 1 | 20.00% |
Mauro Carvalho Chehab | 1 | 0.04% | 1 | 20.00% |
Gustavo A. R. Silva | 1 | 0.04% | 1 | 20.00% |
Total | 2685 | 5 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * ZyDAS ZD1301 driver (demodulator) * * Copyright (C) 2015 Antti Palosaari <crope@iki.fi> */ #include "zd1301_demod.h" static u8 zd1301_demod_gain = 0x38; module_param_named(gain, zd1301_demod_gain, byte, 0644); MODULE_PARM_DESC(gain, "gain (value: 0x00 - 0x70, default: 0x38)"); struct zd1301_demod_dev { struct platform_device *pdev; struct dvb_frontend frontend; struct i2c_adapter adapter; u8 gain; }; static int zd1301_demod_wreg(struct zd1301_demod_dev *dev, u16 reg, u8 val) { struct platform_device *pdev = dev->pdev; struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; return pdata->reg_write(pdata->reg_priv, reg, val); } static int zd1301_demod_rreg(struct zd1301_demod_dev *dev, u16 reg, u8 *val) { struct platform_device *pdev = dev->pdev; struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; return pdata->reg_read(pdata->reg_priv, reg, val); } static int zd1301_demod_set_frontend(struct dvb_frontend *fe) { struct zd1301_demod_dev *dev = fe->demodulator_priv; struct platform_device *pdev = dev->pdev; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u32 if_frequency; u8 r6a50_val; dev_dbg(&pdev->dev, "frequency=%u bandwidth_hz=%u\n", c->frequency, c->bandwidth_hz); /* Program tuner */ if (fe->ops.tuner_ops.set_params && fe->ops.tuner_ops.get_if_frequency) { ret = fe->ops.tuner_ops.set_params(fe); if (ret) goto err; ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); if (ret) goto err; } else { ret = -EINVAL; goto err; } dev_dbg(&pdev->dev, "if_frequency=%u\n", if_frequency); if (if_frequency != 36150000) { ret = -EINVAL; goto err; } switch (c->bandwidth_hz) { case 6000000: r6a50_val = 0x78; break; case 7000000: r6a50_val = 0x68; break; case 8000000: r6a50_val = 0x58; break; default: ret = -EINVAL; goto err; } ret = zd1301_demod_wreg(dev, 0x6a60, 0x11); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a47, 0x46); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a48, 0x46); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a4a, 0x15); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a4b, 0x63); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a5b, 0x99); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a3b, 0x10); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6806, 0x01); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a41, 0x08); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a42, 0x46); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a44, 0x14); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a45, 0x67); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a38, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a4c, 0x52); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a49, 0x2a); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6840, 0x2e); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a50, r6a50_val); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a38, 0x07); if (ret) goto err; return 0; err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static int zd1301_demod_sleep(struct dvb_frontend *fe) { struct zd1301_demod_dev *dev = fe->demodulator_priv; struct platform_device *pdev = dev->pdev; int ret; dev_dbg(&pdev->dev, "\n"); ret = zd1301_demod_wreg(dev, 0x6a43, 0x70); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x684e, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6849, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x68e2, 0xd7); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x68e0, 0x39); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6840, 0x21); if (ret) goto err; return 0; err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static int zd1301_demod_init(struct dvb_frontend *fe) { struct zd1301_demod_dev *dev = fe->demodulator_priv; struct platform_device *pdev = dev->pdev; int ret; dev_dbg(&pdev->dev, "\n"); ret = zd1301_demod_wreg(dev, 0x6840, 0x26); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x68e0, 0xff); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x68e2, 0xd8); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6849, 0x4e); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x684e, 0x01); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6a43, zd1301_demod_gain); if (ret) goto err; return 0; err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static int zd1301_demod_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *settings) { struct zd1301_demod_dev *dev = fe->demodulator_priv; struct platform_device *pdev = dev->pdev; dev_dbg(&pdev->dev, "\n"); /* ~180ms seems to be enough */ settings->min_delay_ms = 400; return 0; } static int zd1301_demod_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct zd1301_demod_dev *dev = fe->demodulator_priv; struct platform_device *pdev = dev->pdev; int ret; u8 u8tmp; ret = zd1301_demod_rreg(dev, 0x6a24, &u8tmp); if (ret) goto err; if (u8tmp > 0x00 && u8tmp < 0x20) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; else *status = 0; dev_dbg(&pdev->dev, "lock byte=%02x\n", u8tmp); /* * Interesting registers here are: * 0x6a05: get some gain value * 0x6a06: get about same gain value than set to 0x6a43 * 0x6a07: get some gain value * 0x6a43: set gain value by driver * 0x6a24: get demod lock bits (FSM stage?) * * Driver should implement some kind of algorithm to calculate suitable * value for register 0x6a43, based likely values from register 0x6a05 * and 0x6a07. Looks like gain register 0x6a43 value could be from * range 0x00 - 0x70. */ if (dev->gain != zd1301_demod_gain) { dev->gain = zd1301_demod_gain; ret = zd1301_demod_wreg(dev, 0x6a43, dev->gain); if (ret) goto err; } return 0; err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static const struct dvb_frontend_ops zd1301_demod_ops = { .delsys = {SYS_DVBT}, .info = { .name = "ZyDAS ZD1301", .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO | FE_CAN_MUTE_TS }, .sleep = zd1301_demod_sleep, .init = zd1301_demod_init, .set_frontend = zd1301_demod_set_frontend, .get_tune_settings = zd1301_demod_get_tune_settings, .read_status = zd1301_demod_read_status, }; struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *pdev) { struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "\n"); return &dev->frontend; } EXPORT_SYMBOL(zd1301_demod_get_dvb_frontend); static int zd1301_demod_i2c_master_xfer(struct i2c_adapter *adapter, struct i2c_msg msg[], int num) { struct zd1301_demod_dev *dev = i2c_get_adapdata(adapter); struct platform_device *pdev = dev->pdev; int ret, i; unsigned long timeout; u8 u8tmp; #define I2C_XFER_TIMEOUT 5 #define ZD1301_IS_I2C_XFER_WRITE_READ(_msg, _num) \ (_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD)) #define ZD1301_IS_I2C_XFER_WRITE(_msg, _num) \ (_num == 1 && !(_msg[0].flags & I2C_M_RD)) #define ZD1301_IS_I2C_XFER_READ(_msg, _num) \ (_num == 1 && (_msg[0].flags & I2C_M_RD)) if (ZD1301_IS_I2C_XFER_WRITE_READ(msg, num)) { dev_dbg(&pdev->dev, "write&read msg[0].len=%u msg[1].len=%u\n", msg[0].len, msg[1].len); if (msg[0].len > 1 || msg[1].len > 8) { ret = -EOPNOTSUPP; goto err; } ret = zd1301_demod_wreg(dev, 0x6811, 0x80); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6812, 0x05); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6813, msg[1].addr << 1); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6801, msg[0].buf[0]); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6802, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6803, 0x06); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6805, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6804, msg[1].len); if (ret) goto err; /* Poll xfer ready */ timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT); for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) { usleep_range(500, 800); ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp); if (ret) goto err; } for (i = 0; i < msg[1].len; i++) { ret = zd1301_demod_rreg(dev, 0x0600 + i, &msg[1].buf[i]); if (ret) goto err; } } else if (ZD1301_IS_I2C_XFER_WRITE(msg, num)) { dev_dbg(&pdev->dev, "write msg[0].len=%u\n", msg[0].len); if (msg[0].len > 1 + 8) { ret = -EOPNOTSUPP; goto err; } ret = zd1301_demod_wreg(dev, 0x6811, 0x80); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6812, 0x01); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6813, msg[0].addr << 1); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6800, msg[0].buf[0]); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6802, 0x00); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6803, 0x06); if (ret) goto err; for (i = 0; i < msg[0].len - 1; i++) { ret = zd1301_demod_wreg(dev, 0x0600 + i, msg[0].buf[1 + i]); if (ret) goto err; } ret = zd1301_demod_wreg(dev, 0x6805, 0x80); if (ret) goto err; ret = zd1301_demod_wreg(dev, 0x6804, msg[0].len - 1); if (ret) goto err; /* Poll xfer ready */ timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT); for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) { usleep_range(500, 800); ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp); if (ret) goto err; } } else { dev_dbg(&pdev->dev, "unknown msg[0].len=%u\n", msg[0].len); ret = -EOPNOTSUPP; goto err; } return num; err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static u32 zd1301_demod_i2c_functionality(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; } static const struct i2c_algorithm zd1301_demod_i2c_algorithm = { .master_xfer = zd1301_demod_i2c_master_xfer, .functionality = zd1301_demod_i2c_functionality, }; struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev) { struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "\n"); return &dev->adapter; } EXPORT_SYMBOL(zd1301_demod_get_i2c_adapter); /* Platform driver interface */ static int zd1301_demod_probe(struct platform_device *pdev) { struct zd1301_demod_dev *dev; struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; int ret; dev_dbg(&pdev->dev, "\n"); if (!pdata) { ret = -EINVAL; dev_err(&pdev->dev, "cannot proceed without platform data\n"); goto err; } if (!pdev->dev.parent->driver) { ret = -EINVAL; dev_dbg(&pdev->dev, "no parent device\n"); goto err; } dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err; } /* Setup the state */ dev->pdev = pdev; dev->gain = zd1301_demod_gain; /* Sleep */ ret = zd1301_demod_wreg(dev, 0x6840, 0x21); if (ret) goto err_kfree; ret = zd1301_demod_wreg(dev, 0x6a38, 0x07); if (ret) goto err_kfree; /* Create I2C adapter */ strscpy(dev->adapter.name, "ZyDAS ZD1301 demod", sizeof(dev->adapter.name)); dev->adapter.algo = &zd1301_demod_i2c_algorithm; dev->adapter.algo_data = NULL; dev->adapter.dev.parent = pdev->dev.parent; i2c_set_adapdata(&dev->adapter, dev); ret = i2c_add_adapter(&dev->adapter); if (ret) { dev_err(&pdev->dev, "I2C adapter add failed %d\n", ret); goto err_kfree; } /* Create dvb frontend */ memcpy(&dev->frontend.ops, &zd1301_demod_ops, sizeof(dev->frontend.ops)); dev->frontend.demodulator_priv = dev; platform_set_drvdata(pdev, dev); dev_info(&pdev->dev, "ZyDAS ZD1301 demod attached\n"); return 0; err_kfree: kfree(dev); err: dev_dbg(&pdev->dev, "failed=%d\n", ret); return ret; } static void zd1301_demod_remove(struct platform_device *pdev) { struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "\n"); i2c_del_adapter(&dev->adapter); kfree(dev); } static struct platform_driver zd1301_demod_driver = { .driver = { .name = "zd1301_demod", .suppress_bind_attrs = true, }, .probe = zd1301_demod_probe, .remove_new = zd1301_demod_remove, }; module_platform_driver(zd1301_demod_driver); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("ZyDAS ZD1301 demodulator driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1