1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 Conexant cx22700 DVB OFDM demodulator driver 4 5 Copyright (C) 2001-2002 Convergence Integrated Media GmbH 6 Holger Waechtler <holger@convergence.de> 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 "cx22700.h" 18 19 20 struct cx22700_state { 21 22 struct i2c_adapter* i2c; 23 24 const struct cx22700_config* config; 25 26 struct dvb_frontend frontend; 27 }; 28 29 30 static int debug; 31 #define dprintk(args...) \ 32 do { \ 33 if (debug) printk(KERN_DEBUG "cx22700: " args); \ 34 } while (0) 35 36 static u8 init_tab [] = { 37 0x04, 0x10, 38 0x05, 0x09, 39 0x06, 0x00, 40 0x08, 0x04, 41 0x09, 0x00, 42 0x0a, 0x01, 43 0x15, 0x40, 44 0x16, 0x10, 45 0x17, 0x87, 46 0x18, 0x17, 47 0x1a, 0x10, 48 0x25, 0x04, 49 0x2e, 0x00, 50 0x39, 0x00, 51 0x3a, 0x04, 52 0x45, 0x08, 53 0x46, 0x02, 54 0x47, 0x05, 55 }; 56 57 58 static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) 59 { 60 int ret; 61 u8 buf [] = { reg, data }; 62 struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; 63 64 dprintk ("%s\n", __func__); 65 66 ret = i2c_transfer (state->i2c, &msg, 1); 67 68 if (ret != 1) 69 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", 70 __func__, reg, data, ret); 71 72 return (ret != 1) ? -1 : 0; 73 } 74 75 static int cx22700_readreg (struct cx22700_state* state, u8 reg) 76 { 77 int ret; 78 u8 b0 [] = { reg }; 79 u8 b1 [] = { 0 }; 80 struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, 81 { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; 82 83 dprintk ("%s\n", __func__); 84 85 ret = i2c_transfer (state->i2c, msg, 2); 86 87 if (ret != 2) return -EIO; 88 89 return b1[0]; 90 } 91 92 static int cx22700_set_inversion (struct cx22700_state* state, int inversion) 93 { 94 u8 val; 95 96 dprintk ("%s\n", __func__); 97 98 switch (inversion) { 99 case INVERSION_AUTO: 100 return -EOPNOTSUPP; 101 case INVERSION_ON: 102 val = cx22700_readreg (state, 0x09); 103 return cx22700_writereg (state, 0x09, val | 0x01); 104 case INVERSION_OFF: 105 val = cx22700_readreg (state, 0x09); 106 return cx22700_writereg (state, 0x09, val & 0xfe); 107 default: 108 return -EINVAL; 109 } 110 } 111 112 static int cx22700_set_tps(struct cx22700_state *state, 113 struct dtv_frontend_properties *p) 114 { 115 static const u8 qam_tab [4] = { 0, 1, 0, 2 }; 116 static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; 117 u8 val; 118 119 dprintk ("%s\n", __func__); 120 121 if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) 122 return -EINVAL; 123 124 if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) 125 return -EINVAL; 126 127 if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) 128 return -EINVAL; 129 130 if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || 131 p->guard_interval > GUARD_INTERVAL_1_4) 132 return -EINVAL; 133 134 if (p->transmission_mode != TRANSMISSION_MODE_2K && 135 p->transmission_mode != TRANSMISSION_MODE_8K) 136 return -EINVAL; 137 138 if (p->modulation != QPSK && 139 p->modulation != QAM_16 && 140 p->modulation != QAM_64) 141 return -EINVAL; 142 143 if ((int)p->hierarchy < HIERARCHY_NONE || 144 p->hierarchy > HIERARCHY_4) 145 return -EINVAL; 146 147 if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000) 148 return -EINVAL; 149 150 if (p->bandwidth_hz == 7000000) 151 cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); 152 else 153 cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); 154 155 val = qam_tab[p->modulation - QPSK]; 156 val |= p->hierarchy - HIERARCHY_NONE; 157 158 cx22700_writereg (state, 0x04, val); 159 160 if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) || 161 p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab)) 162 return -EINVAL; 163 val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; 164 val |= fec_tab[p->code_rate_LP - FEC_1_2]; 165 166 cx22700_writereg (state, 0x05, val); 167 168 val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; 169 val |= p->transmission_mode - TRANSMISSION_MODE_2K; 170 171 cx22700_writereg (state, 0x06, val); 172 173 cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ 174 cx22700_writereg (state, 0x08, 0x04); /* restart acquisition */ 175 176 return 0; 177 } 178 179 static int cx22700_get_tps(struct cx22700_state *state, 180 struct dtv_frontend_properties *p) 181 { 182 static const enum fe_modulation qam_tab[3] = { QPSK, QAM_16, QAM_64 }; 183 static const enum fe_code_rate fec_tab[5] = { 184 FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8 185 }; 186 u8 val; 187 188 dprintk ("%s\n", __func__); 189 190 if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ 191 return -EAGAIN; 192 193 val = cx22700_readreg (state, 0x01); 194 195 if ((val & 0x7) > 4) 196 p->hierarchy = HIERARCHY_AUTO; 197 else 198 p->hierarchy = HIERARCHY_NONE + (val & 0x7); 199 200 if (((val >> 3) & 0x3) > 2) 201 p->modulation = QAM_AUTO; 202 else 203 p->modulation = qam_tab[(val >> 3) & 0x3]; 204 205 val = cx22700_readreg (state, 0x02); 206 207 if (((val >> 3) & 0x07) > 4) 208 p->code_rate_HP = FEC_AUTO; 209 else 210 p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; 211 212 if ((val & 0x07) > 4) 213 p->code_rate_LP = FEC_AUTO; 214 else 215 p->code_rate_LP = fec_tab[val & 0x07]; 216 217 val = cx22700_readreg (state, 0x03); 218 219 p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); 220 p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); 221 222 return 0; 223 } 224 225 static int cx22700_init (struct dvb_frontend* fe) 226 227 { struct cx22700_state* state = fe->demodulator_priv; 228 int i; 229 230 dprintk("cx22700_init: init chip\n"); 231 232 cx22700_writereg (state, 0x00, 0x02); /* soft reset */ 233 cx22700_writereg (state, 0x00, 0x00); 234 235 msleep(10); 236 237 for (i=0; i<sizeof(init_tab); i+=2) 238 cx22700_writereg (state, init_tab[i], init_tab[i+1]); 239 240 cx22700_writereg (state, 0x00, 0x01); 241 242 return 0; 243 } 244 245 static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status) 246 { 247 struct cx22700_state* state = fe->demodulator_priv; 248 249 u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) 250 | (cx22700_readreg (state, 0x0e) << 1); 251 u8 sync = cx22700_readreg (state, 0x07); 252 253 *status = 0; 254 255 if (rs_ber < 0xff00) 256 *status |= FE_HAS_SIGNAL; 257 258 if (sync & 0x20) 259 *status |= FE_HAS_CARRIER; 260 261 if (sync & 0x10) 262 *status |= FE_HAS_VITERBI; 263 264 if (sync & 0x10) 265 *status |= FE_HAS_SYNC; 266 267 if (*status == 0x0f) 268 *status |= FE_HAS_LOCK; 269 270 return 0; 271 } 272 273 static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) 274 { 275 struct cx22700_state* state = fe->demodulator_priv; 276 277 *ber = cx22700_readreg (state, 0x0c) & 0x7f; 278 cx22700_writereg (state, 0x0c, 0x00); 279 280 return 0; 281 } 282 283 static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) 284 { 285 struct cx22700_state* state = fe->demodulator_priv; 286 287 u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) 288 | (cx22700_readreg (state, 0x0e) << 1); 289 *signal_strength = ~rs_ber; 290 291 return 0; 292 } 293 294 static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) 295 { 296 struct cx22700_state* state = fe->demodulator_priv; 297 298 u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) 299 | (cx22700_readreg (state, 0x0e) << 1); 300 *snr = ~rs_ber; 301 302 return 0; 303 } 304 305 static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 306 { 307 struct cx22700_state* state = fe->demodulator_priv; 308 309 *ucblocks = cx22700_readreg (state, 0x0f); 310 cx22700_writereg (state, 0x0f, 0x00); 311 312 return 0; 313 } 314 315 static int cx22700_set_frontend(struct dvb_frontend *fe) 316 { 317 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 318 struct cx22700_state* state = fe->demodulator_priv; 319 320 cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ 321 cx22700_writereg (state, 0x00, 0x00); 322 323 if (fe->ops.tuner_ops.set_params) { 324 fe->ops.tuner_ops.set_params(fe); 325 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 326 } 327 328 cx22700_set_inversion(state, c->inversion); 329 cx22700_set_tps(state, c); 330 cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ 331 cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ 332 333 return 0; 334 } 335 336 static int cx22700_get_frontend(struct dvb_frontend *fe, 337 struct dtv_frontend_properties *c) 338 { 339 struct cx22700_state* state = fe->demodulator_priv; 340 u8 reg09 = cx22700_readreg (state, 0x09); 341 342 c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; 343 return cx22700_get_tps(state, c); 344 } 345 346 static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) 347 { 348 struct cx22700_state* state = fe->demodulator_priv; 349 350 if (enable) { 351 return cx22700_writereg(state, 0x0a, 0x00); 352 } else { 353 return cx22700_writereg(state, 0x0a, 0x01); 354 } 355 } 356 357 static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) 358 { 359 fesettings->min_delay_ms = 150; 360 fesettings->step_size = 166667; 361 fesettings->max_drift = 166667*2; 362 return 0; 363 } 364 365 static void cx22700_release(struct dvb_frontend* fe) 366 { 367 struct cx22700_state* state = fe->demodulator_priv; 368 kfree(state); 369 } 370 371 static const struct dvb_frontend_ops cx22700_ops; 372 373 struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, 374 struct i2c_adapter* i2c) 375 { 376 struct cx22700_state* state = NULL; 377 378 /* allocate memory for the internal state */ 379 state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); 380 if (state == NULL) 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 (cx22700_readreg(state, 0x07) < 0) goto error; 388 389 /* create dvb_frontend */ 390 memcpy(&state->frontend.ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); 391 state->frontend.demodulator_priv = state; 392 return &state->frontend; 393 394 error: 395 kfree(state); 396 return NULL; 397 } 398 399 static const struct dvb_frontend_ops cx22700_ops = { 400 .delsys = { SYS_DVBT }, 401 .info = { 402 .name = "Conexant CX22700 DVB-T", 403 .frequency_min_hz = 470 * MHz, 404 .frequency_max_hz = 860 * MHz, 405 .frequency_stepsize_hz = 166667, 406 .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 407 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 408 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | 409 FE_CAN_RECOVER 410 }, 411 412 .release = cx22700_release, 413 414 .init = cx22700_init, 415 .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, 416 417 .set_frontend = cx22700_set_frontend, 418 .get_frontend = cx22700_get_frontend, 419 .get_tune_settings = cx22700_get_tune_settings, 420 421 .read_status = cx22700_read_status, 422 .read_ber = cx22700_read_ber, 423 .read_signal_strength = cx22700_read_signal_strength, 424 .read_snr = cx22700_read_snr, 425 .read_ucblocks = cx22700_read_ucblocks, 426 }; 427 428 module_param(debug, int, 0644); 429 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 430 431 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); 432 MODULE_AUTHOR("Holger Waechtler"); 433 MODULE_LICENSE("GPL"); 434 435 EXPORT_SYMBOL(cx22700_attach); 436