442 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver4 5 Copyright (C) 2008 Sirius International (Hong Kong) Limited6 Timothy Lee <timothy.lee@siriushk.com>7 8 9*/10 11#include <linux/kernel.h>12#include <linux/init.h>13#include <linux/module.h>14#include <linux/string.h>15#include <linux/slab.h>16#include <media/dvb_frontend.h>17#include "lgs8gl5.h"18 19 20#define REG_RESET 0x0221#define REG_RESET_OFF 0x0122#define REG_03 0x0323#define REG_04 0x0424#define REG_07 0x0725#define REG_09 0x0926#define REG_0A 0x0a27#define REG_0B 0x0b28#define REG_0C 0x0c29#define REG_37 0x3730#define REG_STRENGTH 0x4b31#define REG_STRENGTH_MASK 0x7f32#define REG_STRENGTH_CARRIER 0x8033#define REG_INVERSION 0x7c34#define REG_INVERSION_ON 0x8035#define REG_7D 0x7d36#define REG_7E 0x7e37#define REG_A2 0xa238#define REG_STATUS 0xa439#define REG_STATUS_SYNC 0x0440#define REG_STATUS_LOCK 0x0141 42 43struct lgs8gl5_state {44 struct i2c_adapter *i2c;45 const struct lgs8gl5_config *config;46 struct dvb_frontend frontend;47};48 49 50static int debug;51#define dprintk(args...) \52 do { \53 if (debug) \54 printk(KERN_DEBUG "lgs8gl5: " args); \55 } while (0)56 57 58/* Writes into demod's register */59static int60lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)61{62 int ret;63 u8 buf[] = {reg, data};64 struct i2c_msg msg = {65 .addr = state->config->demod_address,66 .flags = 0,67 .buf = buf,68 .len = 269 };70 71 ret = i2c_transfer(state->i2c, &msg, 1);72 if (ret != 1)73 dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",74 __func__, reg, data, ret);75 return (ret != 1) ? -1 : 0;76}77 78 79/* Reads from demod's register */80static int81lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)82{83 int ret;84 u8 b0[] = {reg};85 u8 b1[] = {0};86 struct i2c_msg msg[2] = {87 {88 .addr = state->config->demod_address,89 .flags = 0,90 .buf = b0,91 .len = 192 },93 {94 .addr = state->config->demod_address,95 .flags = I2C_M_RD,96 .buf = b1,97 .len = 198 }99 };100 101 ret = i2c_transfer(state->i2c, msg, 2);102 if (ret != 2)103 return -EIO;104 105 return b1[0];106}107 108 109static int110lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)111{112 lgs8gl5_read_reg(state, reg);113 lgs8gl5_write_reg(state, reg, data);114 return 0;115}116 117 118/* Writes into alternate device's register */119/* TODO: Find out what that device is for! */120static int121lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)122{123 int ret;124 u8 b0[] = {reg};125 u8 b1[] = {0};126 u8 b2[] = {reg, data};127 struct i2c_msg msg[3] = {128 {129 .addr = state->config->demod_address + 2,130 .flags = 0,131 .buf = b0,132 .len = 1133 },134 {135 .addr = state->config->demod_address + 2,136 .flags = I2C_M_RD,137 .buf = b1,138 .len = 1139 },140 {141 .addr = state->config->demod_address + 2,142 .flags = 0,143 .buf = b2,144 .len = 2145 },146 };147 148 ret = i2c_transfer(state->i2c, msg, 3);149 return (ret != 3) ? -1 : 0;150}151 152 153static void154lgs8gl5_soft_reset(struct lgs8gl5_state *state)155{156 u8 val;157 158 dprintk("%s\n", __func__);159 160 val = lgs8gl5_read_reg(state, REG_RESET);161 lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);162 lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);163 msleep(5);164}165 166 167/* Starts demodulation */168static void169lgs8gl5_start_demod(struct lgs8gl5_state *state)170{171 u8 val;172 int n;173 174 dprintk("%s\n", __func__);175 176 lgs8gl5_update_alt_reg(state, 0xc2, 0x28);177 lgs8gl5_soft_reset(state);178 lgs8gl5_update_reg(state, REG_07, 0x10);179 lgs8gl5_update_reg(state, REG_07, 0x10);180 lgs8gl5_write_reg(state, REG_09, 0x0e);181 lgs8gl5_write_reg(state, REG_0A, 0xe5);182 lgs8gl5_write_reg(state, REG_0B, 0x35);183 lgs8gl5_write_reg(state, REG_0C, 0x30);184 185 lgs8gl5_update_reg(state, REG_03, 0x00);186 lgs8gl5_update_reg(state, REG_7E, 0x01);187 lgs8gl5_update_alt_reg(state, 0xc5, 0x00);188 lgs8gl5_update_reg(state, REG_04, 0x02);189 lgs8gl5_update_reg(state, REG_37, 0x01);190 lgs8gl5_soft_reset(state);191 192 /* Wait for carrier */193 for (n = 0; n < 10; n++) {194 val = lgs8gl5_read_reg(state, REG_STRENGTH);195 dprintk("Wait for carrier[%d] 0x%02X\n", n, val);196 if (val & REG_STRENGTH_CARRIER)197 break;198 msleep(4);199 }200 if (!(val & REG_STRENGTH_CARRIER))201 return;202 203 /* Wait for lock */204 for (n = 0; n < 20; n++) {205 val = lgs8gl5_read_reg(state, REG_STATUS);206 dprintk("Wait for lock[%d] 0x%02X\n", n, val);207 if (val & REG_STATUS_LOCK)208 break;209 msleep(12);210 }211 if (!(val & REG_STATUS_LOCK))212 return;213 214 lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));215 lgs8gl5_soft_reset(state);216}217 218 219static int220lgs8gl5_init(struct dvb_frontend *fe)221{222 struct lgs8gl5_state *state = fe->demodulator_priv;223 224 dprintk("%s\n", __func__);225 226 lgs8gl5_update_alt_reg(state, 0xc2, 0x28);227 lgs8gl5_soft_reset(state);228 lgs8gl5_update_reg(state, REG_07, 0x10);229 lgs8gl5_update_reg(state, REG_07, 0x10);230 lgs8gl5_write_reg(state, REG_09, 0x0e);231 lgs8gl5_write_reg(state, REG_0A, 0xe5);232 lgs8gl5_write_reg(state, REG_0B, 0x35);233 lgs8gl5_write_reg(state, REG_0C, 0x30);234 235 return 0;236}237 238 239static int240lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)241{242 struct lgs8gl5_state *state = fe->demodulator_priv;243 u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);244 u8 flags = lgs8gl5_read_reg(state, REG_STATUS);245 246 *status = 0;247 248 if ((level & REG_STRENGTH_MASK) > 0)249 *status |= FE_HAS_SIGNAL;250 if (level & REG_STRENGTH_CARRIER)251 *status |= FE_HAS_CARRIER;252 if (flags & REG_STATUS_SYNC)253 *status |= FE_HAS_SYNC;254 if (flags & REG_STATUS_LOCK)255 *status |= FE_HAS_LOCK;256 257 return 0;258}259 260 261static int262lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)263{264 *ber = 0;265 266 return 0;267}268 269 270static int271lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)272{273 struct lgs8gl5_state *state = fe->demodulator_priv;274 u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);275 *signal_strength = (level & REG_STRENGTH_MASK) << 8;276 277 return 0;278}279 280 281static int282lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)283{284 struct lgs8gl5_state *state = fe->demodulator_priv;285 u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);286 *snr = (level & REG_STRENGTH_MASK) << 8;287 288 return 0;289}290 291 292static int293lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)294{295 *ucblocks = 0;296 297 return 0;298}299 300 301static int302lgs8gl5_set_frontend(struct dvb_frontend *fe)303{304 struct dtv_frontend_properties *p = &fe->dtv_property_cache;305 struct lgs8gl5_state *state = fe->demodulator_priv;306 307 dprintk("%s\n", __func__);308 309 if (p->bandwidth_hz != 8000000)310 return -EINVAL;311 312 if (fe->ops.tuner_ops.set_params) {313 fe->ops.tuner_ops.set_params(fe);314 if (fe->ops.i2c_gate_ctrl)315 fe->ops.i2c_gate_ctrl(fe, 0);316 }317 318 /* lgs8gl5_set_inversion(state, p->inversion); */319 320 lgs8gl5_start_demod(state);321 322 return 0;323}324 325 326static int327lgs8gl5_get_frontend(struct dvb_frontend *fe,328 struct dtv_frontend_properties *p)329{330 struct lgs8gl5_state *state = fe->demodulator_priv;331 332 u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);333 334 p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;335 336 p->code_rate_HP = FEC_1_2;337 p->code_rate_LP = FEC_7_8;338 p->guard_interval = GUARD_INTERVAL_1_32;339 p->transmission_mode = TRANSMISSION_MODE_2K;340 p->modulation = QAM_64;341 p->hierarchy = HIERARCHY_NONE;342 p->bandwidth_hz = 8000000;343 344 return 0;345}346 347 348static int349lgs8gl5_get_tune_settings(struct dvb_frontend *fe,350 struct dvb_frontend_tune_settings *fesettings)351{352 fesettings->min_delay_ms = 240;353 fesettings->step_size = 0;354 fesettings->max_drift = 0;355 return 0;356}357 358 359static void360lgs8gl5_release(struct dvb_frontend *fe)361{362 struct lgs8gl5_state *state = fe->demodulator_priv;363 kfree(state);364}365 366 367static const struct dvb_frontend_ops lgs8gl5_ops;368 369 370struct dvb_frontend*371lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)372{373 struct lgs8gl5_state *state = NULL;374 375 dprintk("%s\n", __func__);376 377 /* Allocate memory for the internal state */378 state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);379 if (state == NULL)380 goto error;381 382 /* Setup the state */383 state->config = config;384 state->i2c = i2c;385 386 /* Check if the demod is there */387 if (lgs8gl5_read_reg(state, REG_RESET) < 0)388 goto error;389 390 /* Create dvb_frontend */391 memcpy(&state->frontend.ops, &lgs8gl5_ops,392 sizeof(struct dvb_frontend_ops));393 state->frontend.demodulator_priv = state;394 return &state->frontend;395 396error:397 kfree(state);398 return NULL;399}400EXPORT_SYMBOL(lgs8gl5_attach);401 402 403static const struct dvb_frontend_ops lgs8gl5_ops = {404 .delsys = { SYS_DTMB },405 .info = {406 .name = "Legend Silicon LGS-8GL5 DMB-TH",407 .frequency_min_hz = 474 * MHz,408 .frequency_max_hz = 858 * MHz,409 .frequency_stepsize_hz = 10 * kHz,410 .caps = FE_CAN_FEC_AUTO |411 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |412 FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |413 FE_CAN_TRANSMISSION_MODE_AUTO |414 FE_CAN_BANDWIDTH_AUTO |415 FE_CAN_GUARD_INTERVAL_AUTO |416 FE_CAN_HIERARCHY_AUTO |417 FE_CAN_RECOVER418 },419 420 .release = lgs8gl5_release,421 422 .init = lgs8gl5_init,423 424 .set_frontend = lgs8gl5_set_frontend,425 .get_frontend = lgs8gl5_get_frontend,426 .get_tune_settings = lgs8gl5_get_tune_settings,427 428 .read_status = lgs8gl5_read_status,429 .read_ber = lgs8gl5_read_ber,430 .read_signal_strength = lgs8gl5_read_signal_strength,431 .read_snr = lgs8gl5_read_snr,432 .read_ucblocks = lgs8gl5_read_ucblocks,433};434 435 436module_param(debug, int, 0644);437MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");438 439MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");440MODULE_AUTHOR("Timothy Lee");441MODULE_LICENSE("GPL");442