Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (9365): adding lgdt3304 based driver

lgdt3304.c:
frontend driver for the lgdt3304 chip, this driver is not compatible
with the lgdt330x.

Signed-off-by: Markus Rechberger <mrechberger@sundtek.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Markus Rechberger and committed by
Mauro Carvalho Chehab
67ea14f2 aa56cb9d

+430
+7
drivers/media/video/empia/lgdt3304/Makefile
··· 1 + lgdt3304-demod-objs := lgdt3304.o 2 + 3 + obj-m += lgdt3304-demod.o 4 + 5 + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core 6 + EXTRA_CFLAGS += -Idrivers/media/dvb/frontends 7 + EXTRA_CFLAGS += -DCONFIG_DVB_LGDT3304
+378
drivers/media/video/empia/lgdt3304/lgdt3304.c
··· 1 + /* 2 + * Driver for LG ATSC lgdt3304 driver 3 + * 4 + * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> 5 + * 6 + */ 7 + 8 + #include <linux/kernel.h> 9 + #include <linux/module.h> 10 + #include <linux/delay.h> 11 + #include "dvb_frontend.h" 12 + #include "lgdt3304.h" 13 + 14 + static unsigned int debug = 0; 15 + module_param(debug, int, 0644); 16 + MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)"); 17 + 18 + #define dprintk(fmt, args...) if (debug) do {\ 19 + printk("lgdt3304 debug: " fmt, ##args); } while (0) 20 + 21 + struct lgdt3304_state 22 + { 23 + struct dvb_frontend frontend; 24 + fe_modulation_t current_modulation; 25 + __u32 snr; 26 + __u32 current_frequency; 27 + __u8 addr; 28 + struct i2c_adapter *i2c; 29 + }; 30 + 31 + static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len) 32 + { 33 + struct lgdt3304_state *state = fe->demodulator_priv; 34 + struct i2c_msg i2cmsgs = { 35 + .addr = state->addr, 36 + .flags = 0, 37 + .len = 3, 38 + .buf = buf 39 + }; 40 + int i; 41 + int err; 42 + 43 + for (i=0; i<len-1; i+=3){ 44 + if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { 45 + printk("%s i2c_transfer error %d\n", __FUNCTION__, err); 46 + if (err < 0) 47 + return err; 48 + else 49 + return -EREMOTEIO; 50 + } 51 + i2cmsgs.buf += 3; 52 + } 53 + return 0; 54 + } 55 + 56 + static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg) 57 + { 58 + struct lgdt3304_state *state = fe->demodulator_priv; 59 + struct i2c_msg i2cmsgs[2]; 60 + int ret; 61 + __u8 buf; 62 + 63 + __u8 regbuf[2] = { reg>>8, reg&0xff }; 64 + 65 + i2cmsgs[0].addr = state->addr; 66 + i2cmsgs[0].flags = 0; 67 + i2cmsgs[0].len = 2; 68 + i2cmsgs[0].buf = regbuf; 69 + 70 + i2cmsgs[1].addr = state->addr; 71 + i2cmsgs[1].flags = I2C_M_RD; 72 + i2cmsgs[1].len = 1; 73 + i2cmsgs[1].buf = &buf; 74 + 75 + if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) { 76 + printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); 77 + return ret; 78 + } 79 + 80 + return buf; 81 + } 82 + 83 + static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val) 84 + { 85 + struct lgdt3304_state *state = fe->demodulator_priv; 86 + char buffer[3] = { reg>>8, reg&0xff, val }; 87 + int ret; 88 + 89 + struct i2c_msg i2cmsgs = { 90 + .addr = state->addr, 91 + .flags = 0, 92 + .len = 3, 93 + .buf=buffer 94 + }; 95 + ret = i2c_transfer(state->i2c, &i2cmsgs, 1); 96 + if (ret != 1) { 97 + printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); 98 + return ret; 99 + } 100 + 101 + return 0; 102 + } 103 + 104 + 105 + static int lgdt3304_soft_Reset(struct dvb_frontend *fe) 106 + { 107 + lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a); 108 + lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b); 109 + mdelay(200); 110 + return 0; 111 + } 112 + 113 + static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { 114 + int err = 0; 115 + 116 + static __u8 lgdt3304_vsb8_data[] = { 117 + /* 16bit , 8bit */ 118 + /* regs , val */ 119 + 0x00, 0x00, 0x02, 120 + 0x00, 0x00, 0x13, 121 + 0x00, 0x0d, 0x02, 122 + 0x00, 0x0e, 0x02, 123 + 0x00, 0x12, 0x32, 124 + 0x00, 0x13, 0xc4, 125 + 0x01, 0x12, 0x17, 126 + 0x01, 0x13, 0x15, 127 + 0x01, 0x14, 0x18, 128 + 0x01, 0x15, 0xff, 129 + 0x01, 0x16, 0x2c, 130 + 0x02, 0x14, 0x67, 131 + 0x02, 0x24, 0x8d, 132 + 0x04, 0x27, 0x12, 133 + 0x04, 0x28, 0x4f, 134 + 0x03, 0x08, 0x80, 135 + 0x03, 0x09, 0x00, 136 + 0x03, 0x0d, 0x00, 137 + 0x03, 0x0e, 0x1c, 138 + 0x03, 0x14, 0xe1, 139 + 0x05, 0x0e, 0x5b, 140 + }; 141 + 142 + /* not yet tested .. */ 143 + static __u8 lgdt3304_qam64_data[] = { 144 + /* 16bit , 8bit */ 145 + /* regs , val */ 146 + 0x00, 0x00, 0x18, 147 + 0x00, 0x0d, 0x02, 148 + //0x00, 0x0e, 0x02, 149 + 0x00, 0x12, 0x2a, 150 + 0x00, 0x13, 0x00, 151 + 0x03, 0x14, 0xe3, 152 + 0x03, 0x0e, 0x1c, 153 + 0x03, 0x08, 0x66, 154 + 0x03, 0x09, 0x66, 155 + 0x03, 0x0a, 0x08, 156 + 0x03, 0x0b, 0x9b, 157 + 0x05, 0x0e, 0x5b, 158 + }; 159 + 160 + 161 + /* tested with KWorld a340 */ 162 + static __u8 lgdt3304_qam256_data[] = { 163 + /* 16bit , 8bit */ 164 + /* regs , val */ 165 + 0x00, 0x00, 0x01, //0x19, 166 + 0x00, 0x12, 0x2a, 167 + 0x00, 0x13, 0x80, 168 + 0x00, 0x0d, 0x02, 169 + 0x03, 0x14, 0xe3, 170 + 171 + 0x03, 0x0e, 0x1c, 172 + 0x03, 0x08, 0x66, 173 + 0x03, 0x09, 0x66, 174 + 0x03, 0x0a, 0x08, 175 + 0x03, 0x0b, 0x9b, 176 + 177 + 0x03, 0x0d, 0x14, 178 + //0x05, 0x0e, 0x5b, 179 + 0x01, 0x06, 0x4a, 180 + 0x01, 0x07, 0x3d, 181 + 0x01, 0x08, 0x70, 182 + 0x01, 0x09, 0xa3, 183 + 184 + 0x05, 0x04, 0xfd, 185 + 186 + 0x00, 0x0d, 0x82, 187 + 188 + 0x05, 0x0e, 0x5b, 189 + 190 + 0x05, 0x0e, 0x5b, 191 + 192 + 0x00, 0x02, 0x9a, 193 + 194 + 0x00, 0x02, 0x9b, 195 + 196 + 0x00, 0x00, 0x01, 197 + 0x00, 0x12, 0x2a, 198 + 0x00, 0x13, 0x80, 199 + 0x00, 0x0d, 0x02, 200 + 0x03, 0x14, 0xe3, 201 + 202 + 0x03, 0x0e, 0x1c, 203 + 0x03, 0x08, 0x66, 204 + 0x03, 0x09, 0x66, 205 + 0x03, 0x0a, 0x08, 206 + 0x03, 0x0b, 0x9b, 207 + 208 + 0x03, 0x0d, 0x14, 209 + 0x01, 0x06, 0x4a, 210 + 0x01, 0x07, 0x3d, 211 + 0x01, 0x08, 0x70, 212 + 0x01, 0x09, 0xa3, 213 + 214 + 0x05, 0x04, 0xfd, 215 + 216 + 0x00, 0x0d, 0x82, 217 + 218 + 0x05, 0x0e, 0x5b, 219 + }; 220 + 221 + struct lgdt3304_state *state = fe->demodulator_priv; 222 + if (state->current_modulation != param->u.vsb.modulation) { 223 + switch(param->u.vsb.modulation) { 224 + case VSB_8: 225 + err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data, 226 + sizeof(lgdt3304_vsb8_data)); 227 + break; 228 + case QAM_64: 229 + err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data, 230 + sizeof(lgdt3304_qam64_data)); 231 + break; 232 + case QAM_256: 233 + err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data, 234 + sizeof(lgdt3304_qam256_data)); 235 + break; 236 + default: 237 + break; 238 + } 239 + 240 + if (err) { 241 + printk("%s error setting modulation\n", __FUNCTION__); 242 + } else { 243 + state->current_modulation = param->u.vsb.modulation; 244 + } 245 + } 246 + state->current_frequency = param->frequency; 247 + 248 + lgdt3304_soft_Reset(fe); 249 + 250 + 251 + if (fe->ops.tuner_ops.set_params) 252 + fe->ops.tuner_ops.set_params(fe, param); 253 + 254 + return 0; 255 + } 256 + 257 + static int lgdt3304_init(struct dvb_frontend *fe) { 258 + return 0; 259 + } 260 + 261 + static int lgdt3304_sleep(struct dvb_frontend *fe) { 262 + return 0; 263 + } 264 + 265 + 266 + static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status) 267 + { 268 + struct lgdt3304_state *state = fe->demodulator_priv; 269 + int r011d; 270 + int qam_lck; 271 + 272 + *status = 0; 273 + dprintk("lgdt read status\n"); 274 + 275 + r011d = lgdt3304_i2c_read_reg(fe, 0x011d); 276 + 277 + dprintk("%02x\n", r011d); 278 + 279 + switch(state->current_modulation) { 280 + case VSB_8: 281 + if (r011d & 0x80) { 282 + dprintk("VSB Locked\n"); 283 + *status |= FE_HAS_CARRIER; 284 + *status |= FE_HAS_LOCK; 285 + *status |= FE_HAS_SYNC; 286 + *status |= FE_HAS_SIGNAL; 287 + } 288 + break; 289 + case QAM_64: 290 + case QAM_256: 291 + qam_lck = r011d & 0x7; 292 + switch(qam_lck) { 293 + case 0x0: dprintk("Unlock\n"); 294 + break; 295 + case 0x4: dprintk("1st Lock in acquisition state\n"); 296 + break; 297 + case 0x6: dprintk("2nd Lock in acquisition state\n"); 298 + break; 299 + case 0x7: dprintk("Final Lock in good reception state\n"); 300 + *status |= FE_HAS_CARRIER; 301 + *status |= FE_HAS_LOCK; 302 + *status |= FE_HAS_SYNC; 303 + *status |= FE_HAS_SIGNAL; 304 + break; 305 + } 306 + break; 307 + default: 308 + printk("%s unhandled modulation\n", __FUNCTION__); 309 + } 310 + 311 + 312 + return 0; 313 + } 314 + 315 + static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber) 316 + { 317 + dprintk("read ber\n"); 318 + return 0; 319 + } 320 + 321 + static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr) 322 + { 323 + dprintk("read snr\n"); 324 + return 0; 325 + } 326 + 327 + static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks) 328 + { 329 + dprintk("read ucblocks\n"); 330 + return 0; 331 + } 332 + 333 + static void lgdt3304_release(struct dvb_frontend *fe) 334 + { 335 + struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv; 336 + kfree(state); 337 + } 338 + 339 + static struct dvb_frontend_ops demod_lgdt3304={ 340 + .info = { 341 + .name = "LG 3304", 342 + .type = FE_ATSC, 343 + .frequency_min = 54000000, 344 + .frequency_max = 858000000, 345 + .frequency_stepsize = 62500, 346 + .symbol_rate_min = 5056941, 347 + .symbol_rate_max = 10762000, 348 + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB 349 + }, 350 + .init = lgdt3304_init, 351 + .sleep = lgdt3304_sleep, 352 + .set_frontend = lgdt3304_set_parameters, 353 + .read_snr = lgdt3304_read_snr, 354 + .read_ber = lgdt3304_read_ber, 355 + .read_status = lgdt3304_read_status, 356 + .read_ucblocks = lgdt3304_read_ucblocks, 357 + .release = lgdt3304_release, 358 + }; 359 + 360 + struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, 361 + struct i2c_adapter *i2c) 362 + { 363 + 364 + struct lgdt3304_state *state; 365 + state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); 366 + memset(state, 0x0, sizeof(struct lgdt3304_state)); 367 + state->addr = config->i2c_address; 368 + state->i2c = i2c; 369 + 370 + memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops)); 371 + state->frontend.demodulator_priv = state; 372 + return &state->frontend; 373 + } 374 + 375 + EXPORT_SYMBOL_GPL(lgdt3304_attach); 376 + MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>"); 377 + MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver"); 378 + MODULE_LICENSE("GPL");
+45
drivers/media/video/empia/lgdt3304/lgdt3304.h
··· 1 + /* 2 + * Driver for DVB-T lgdt3304 demodulator 3 + * 4 + * Copyright (C) 2008 Markus Rechberger <mrechberger@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= 20 + */ 21 + 22 + #ifndef LGDT3304_H 23 + #define LGDT3304_H 24 + 25 + #include <linux/dvb/frontend.h> 26 + 27 + struct lgdt3304_config 28 + { 29 + /* demodulator's I2C address */ 30 + u8 i2c_address; 31 + }; 32 + 33 + #if defined(CONFIG_DVB_LGDT3304) || (defined(CONFIG_DVB_LGDT3304_MODULE) && defined(MODULE)) 34 + extern struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, 35 + struct i2c_adapter *i2c); 36 + #else 37 + static inline struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, 38 + struct i2c_adapter *i2c) 39 + { 40 + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); 41 + return NULL; 42 + } 43 + #endif /* CONFIG_DVB_LGDT */ 44 + 45 + #endif /* LGDT3304_H */