From d7009cdc286d7c9714b357b0e85b3fcbbf9e03a4 Mon Sep 17 00:00:00 2001 From: "Beholder Intl. Ltd. Dmitry Belimov" Date: Thu, 24 Sep 2009 13:13:28 -0300 Subject: V4L/DVB (13075): xc5000: add FM radio support Add FM radio for the xc5000 silicon tuner chip. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 66 +++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index f4ffcdc9b848..fc7cb74256d0 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -739,15 +739,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) return ret; } -static int xc5000_set_analog_params(struct dvb_frontend *fe, +static int xc5000_set_tv_freq(struct dvb_frontend *fe, struct analog_parameters *params) { struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); - dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", __func__, params->frequency); @@ -827,6 +824,67 @@ tune_channel: return 0; } +static int xc5000_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + dprintk(1, "%s() frequency=%d (in units of khz)\n", + __func__, params->frequency); + + priv->freq_hz = params->frequency * 125 / 2; + + priv->rf_mode = XC_RF_MODE_AIR; + + ret = xc_SetTVStandard(priv, + XC5000_Standard[FM_Radio_INPUT1].VideoMode, + XC5000_Standard[FM_Radio_INPUT1].AudioMode); + + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + return -EREMOTEIO; + } + + ret = xc_SetSignalSource(priv, priv->rf_mode); + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR + "xc5000: xc_SetSignalSource(%d) failed\n", + priv->rf_mode); + return -EREMOTEIO; + } + + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); + + return 0; +} + +static int xc5000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) + xc_load_fw_and_init_tuner(fe); + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = xc5000_set_radio_freq(fe, params); + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = xc5000_set_tv_freq(fe, params); + break; + } + + return ret; +} + + static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; -- cgit v1.2.1 From 496e9057adf55d0e6c9be3805f0bc61288409317 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 24 Sep 2009 13:27:24 -0300 Subject: V4L/DVB (13076): xc5000: make the definition of the FM input part of the xc5000 config struct Remove hard-coded definition of the xc5000 FM radio input, making it a parameter passed in when doing the attach call. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 25 ++++++++++++++++++++++--- drivers/media/common/tuners/xc5000.h | 6 ++++++ 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index fc7cb74256d0..d33bf831e76c 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -61,6 +61,7 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; + u8 radio_input; }; /* Misc Defines */ @@ -829,17 +830,32 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, { struct xc5000_priv *priv = fe->tuner_priv; int ret = -EINVAL; + u8 radio_input; dprintk(1, "%s() frequency=%d (in units of khz)\n", __func__, params->frequency); + if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) { + dprintk(1, "%s() radio input not configured\n", __func__); + return -EINVAL; + } + + if (priv->radio_input == XC5000_RADIO_FM1) + radio_input = FM_Radio_INPUT1; + else if (priv->radio_input == XC5000_RADIO_FM2) + radio_input = FM_Radio_INPUT2; + else { + dprintk(1, "%s() unknown radio input %d\n", __func__, + priv->radio_input); + return -EINVAL; + } + priv->freq_hz = params->frequency * 125 / 2; priv->rf_mode = XC_RF_MODE_AIR; - ret = xc_SetTVStandard(priv, - XC5000_Standard[FM_Radio_INPUT1].VideoMode, - XC5000_Standard[FM_Radio_INPUT1].AudioMode); + ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, + XC5000_Standard[radio_input].AudioMode); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); @@ -1058,6 +1074,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->radio_input == 0) + priv->radio_input = cfg->radio_input; + /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index f4c146698a00..e6d7236c9ea1 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -30,11 +30,17 @@ struct i2c_adapter; struct xc5000_config { u8 i2c_address; u32 if_khz; + u8 radio_input; }; /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 +/* Possible Radio inputs */ +#define XC5000_RADIO_NOT_CONFIGURED 0 +#define XC5000_RADIO_FM1 1 +#define XC5000_RADIO_FM2 2 + /* For each bridge framework, when it attaches either analog or digital, * it has to store a reference back to its _core equivalent structure, * so that it can service the hardware by steering gpio's etc. -- cgit v1.2.1 From 1d23a002434802078d806ddc2937bd69bbbd6dc8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 27 Sep 2009 20:05:23 -0300 Subject: V4L/DVB (13099): ir-functions: Export ir_rc5_decode() for use by the cx23885 module Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-functions.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index abd4791acb0e..fc2d8941459f 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -275,7 +275,7 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase); * saa7134 */ /* decode raw bit pattern to RC5 code */ -static u32 ir_rc5_decode(unsigned int code) +u32 ir_rc5_decode(unsigned int code) { unsigned int org_code = code; unsigned int pair; @@ -304,6 +304,7 @@ static u32 ir_rc5_decode(unsigned int code) RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5; } +EXPORT_SYMBOL_GPL(ir_rc5_decode); void ir_rc5_timer_end(unsigned long data) { -- cgit v1.2.1 From b450b92e162a454b82fea6373c83e447e28ce4d6 Mon Sep 17 00:00:00 2001 From: Henk Vergonet Date: Sun, 27 Sep 2009 18:19:58 -0300 Subject: V4L/DVB (13108): tda8290: enable deemphasis_50 module parameter This adds a forgotten module_param macro needed to set a deemphasis of 50us. It is the standard setting for commercial FM radio broadcasts outside the US. Signed-off-by: Henk Vergonet Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda8290.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 064d14c8d7b2..c190b0dedee4 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); static int deemphasis_50; +module_param(deemphasis_50, int, 0644); MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); /* ---------------------------------------------------------------------- */ -- cgit v1.2.1 From 3986bd116f3c53d695aef1781e14b6c5670d4cdd Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 27 Sep 2009 17:00:13 -0300 Subject: V4L/DVB (13110): tda18271: use temporary variables in tda18271_rf_tracking_filters_init Use temporary variables for signed calulations between unsigned values Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 3a50ce96fcb9..8b515a7d93f8 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -572,6 +572,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int bcal, rf, i; + s32 divisor, dividend; #define RF1 0 #define RF2 1 #define RF3 2 @@ -614,15 +615,17 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: - map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - - prog_cal[RF1] + prog_tab[RF1]) / - (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000); + dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) - + (s32)(prog_cal[RF1] + prog_tab[RF1]); + divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; + map[i].rf_a1 = (dividend / divisor); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: - map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - - prog_cal[RF2] + prog_tab[RF2]) / - (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000); + dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) - + (s32)(prog_cal[RF2] + prog_tab[RF2]); + divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; + map[i].rf_a2 = (dividend / divisor); map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; map[i].rf3 = rf_freq[RF3] / 1000; break; -- cgit v1.2.1 From 3a6b49fef6cd18ce3de9de3db12bfbeacf39f9e9 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 27 Sep 2009 23:10:20 -0300 Subject: V4L/DVB (13111): tda18271: more signedness fixes Convert tda18271_rf_tracking_filter_cal.rf_[ab][12] from int to s32. Convert tda18271_priv.tm_rfcal from unsigned int to u8. Cast subtractions between u32 values as s32. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 21 +++++++++++---------- drivers/media/common/tuners/tda18271-priv.h | 11 ++++++----- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 8b515a7d93f8..e0fd9f5d5944 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - int tm_current, rfcal_comp, approx, i, ret; - u8 dc_over_dt, rf_tab; + int i, ret; + u8 tm_current, dc_over_dt, rf_tab; + s32 rfcal_comp, approx; /* power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); @@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { - approx = map[i].rf_a1 * - (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; + approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + + map[i].rf_b1 + rf_tab; } else { - approx = map[i].rf_a2 * - (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; + approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + + map[i].rf_b2 + rf_tab; } if (approx < 0) @@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; + rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; - regs[R_EB14] = approx + rfcal_comp; + regs[R_EB14] = (unsigned char)(approx + rfcal_comp); ret = tda18271_write_regs(fe, R_EB14, 1); fail: return ret; @@ -611,7 +612,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) switch (rf) { case RF1: map[i].rf_a1 = 0; - map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; + map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]); map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: @@ -626,7 +627,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) (s32)(prog_cal[RF2] + prog_tab[RF2]); divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; map[i].rf_a2 = (dividend / divisor); - map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; + map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]); map[i].rf3 = rf_freq[RF3] / 1000; break; default: diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 2bee229acd91..74075be4dea3 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal { u32 rf1; u32 rf2; u32 rf3; - int rf_a1; - int rf_b1; - int rf_a2; - int rf_b2; + s32 rf_a1; + s32 rf_b1; + s32 rf_a2; + s32 rf_b2; }; enum tda18271_pll { @@ -111,10 +111,11 @@ struct tda18271_priv { enum tda18271_output_options output_opt; unsigned int config; /* interface to saa713x / tda829x */ - unsigned int tm_rfcal; unsigned int cal_initialized:1; unsigned int small_i2c:1; + u8 tm_rfcal; + struct tda18271_map_layout *maps; struct tda18271_std_map std; struct tda18271_rf_tracking_filter_cal rf_cal_state[8]; -- cgit v1.2.1 From c0faeee5a3b70c894a27fa81a0d98a3ab223aff0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 28 Sep 2009 03:13:49 -0300 Subject: V4L/DVB (13112): tda18271: display some state information in debug output Display i2c adapter id, address and master / slave role in debug output. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-maps.c | 1 + drivers/media/common/tuners/tda18271-priv.h | 34 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index e21fdeff3ddf..e7f84c705da8 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = { int tda18271_lookup_cid_target(struct dvb_frontend *fe, u32 *freq, u8 *cid_target, u16 *count_limit) { + struct tda18271_priv *priv = fe->tuner_priv; int i = 0; while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 74075be4dea3..7047680cdbd1 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -136,27 +136,37 @@ extern int tda18271_debug; #define DBG_ADV 8 #define DBG_CAL 16 -#define tda_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt, __func__, ##arg) - -#define tda_dprintk(lvl, fmt, arg...) do {\ +#define tda_printk(st, kern, fmt, arg...) do {\ + if (st) { \ + struct tda18271_priv *state = st; \ + printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \ + i2c_adapter_id(state->i2c_props.adap), \ + state->i2c_props.addr, \ + (state->role == TDA18271_MASTER) \ + ? "M" : "S", ##arg); \ + } else \ + printk(kern "%s: " fmt, __func__, ##arg); \ +} while (0) + +#define tda_dprintk(st, lvl, fmt, arg...) do {\ if (tda18271_debug & lvl) \ - tda_printk(KERN_DEBUG, fmt, ##arg); } while (0) + tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0) #define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) -#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg) -#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg) -#define tda_dbg(fmt, arg...) tda_dprintk(DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) tda_dprintk(DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) tda_dprintk(DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) tda_dprintk(DBG_CAL, fmt, ##arg) +#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) #define tda_fail(ret) \ ({ \ int __ret; \ __ret = (ret < 0); \ if (__ret) \ - tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\ + tda_printk(priv, KERN_ERR, \ + "error %d on line %d\n", ret, __LINE__); \ __ret; \ }) -- cgit v1.2.1 From d8d8622552088ca94fab4e4997f948514d0bdc27 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sat, 19 Sep 2009 09:51:12 -0300 Subject: V4L/DVB (13134): Add support for TBS-likes remotes The patch brings infrared remote support for some cx88 based cards. Such as: TeVii S460,S420; Omicom SS4; SatTrade ST4200; TBS 8920,8910; Prof 7300,6200. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index f6790172736a..a2c80a7a3ae4 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2964,6 +2964,46 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = { }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); +static struct ir_scancode ir_codes_tbs_nec[] = { + { 0x04, KEY_POWER2}, /*power*/ + { 0x14, KEY_MUTE}, /*mute*/ + { 0x07, KEY_1}, + { 0x06, KEY_2}, + { 0x05, KEY_3}, + { 0x0b, KEY_4}, + { 0x0a, KEY_5}, + { 0x09, KEY_6}, + { 0x0f, KEY_7}, + { 0x0e, KEY_8}, + { 0x0d, KEY_9}, + { 0x12, KEY_0}, + { 0x16, KEY_CHANNELUP}, /*ch+*/ + { 0x11, KEY_CHANNELDOWN},/*ch-*/ + { 0x13, KEY_VOLUMEUP}, /*vol+*/ + { 0x0c, KEY_VOLUMEDOWN},/*vol-*/ + { 0x03, KEY_RECORD}, /*rec*/ + { 0x18, KEY_PAUSE}, /*pause*/ + { 0x19, KEY_OK}, /*ok*/ + { 0x1a, KEY_CAMERA}, /* snapshot */ + { 0x01, KEY_UP}, + { 0x10, KEY_LEFT}, + { 0x02, KEY_RIGHT}, + { 0x08, KEY_DOWN}, + { 0x15, KEY_FAVORITES}, + { 0x17, KEY_SUBTITLE}, + { 0x1d, KEY_ZOOM}, + { 0x1f, KEY_EXIT}, + { 0x1e, KEY_MENU}, + { 0x1c, KEY_EPG}, + { 0x00, KEY_PREVIOUS}, + { 0x1b, KEY_MODE}, +}; +struct ir_scancode_table ir_codes_tbs_nec_table = { + .scan = ir_codes_tbs_nec, + .size = ARRAY_SIZE(ir_codes_tbs_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table); + /* Terratec Cinergy Hybrid T USB XS Devin Heitmueller */ -- cgit v1.2.1 From 9d2ba7ad802300d6a1830df9268d8ba478c66a18 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Wed, 23 Sep 2009 14:44:12 -0300 Subject: V4L/DVB (13135): Add support for TeVii remotes The patch brings infrared remote support for some cx88 based cards. Such as TeVii S460,S420. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index a2c80a7a3ae4..32f765e6e5fd 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2964,6 +2964,61 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = { }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); +static struct ir_scancode ir_codes_tevii_nec[] = { + { 0x0a, KEY_POWER2}, + { 0x0c, KEY_MUTE}, + { 0x11, KEY_1}, + { 0x12, KEY_2}, + { 0x13, KEY_3}, + { 0x14, KEY_4}, + { 0x15, KEY_5}, + { 0x16, KEY_6}, + { 0x17, KEY_7}, + { 0x18, KEY_8}, + { 0x19, KEY_9}, + { 0x10, KEY_0}, + { 0x1c, KEY_MENU}, + { 0x0f, KEY_VOLUMEDOWN}, + { 0x1a, KEY_LAST}, + { 0x0e, KEY_OPEN}, + { 0x04, KEY_RECORD}, + { 0x09, KEY_VOLUMEUP}, + { 0x08, KEY_CHANNELUP}, + { 0x07, KEY_PVR}, + { 0x0b, KEY_TIME}, + { 0x02, KEY_RIGHT}, + { 0x03, KEY_LEFT}, + { 0x00, KEY_UP}, + { 0x1f, KEY_OK}, + { 0x01, KEY_DOWN}, + { 0x05, KEY_TUNER}, + { 0x06, KEY_CHANNELDOWN}, + { 0x40, KEY_PLAYPAUSE}, + { 0x1e, KEY_REWIND}, + { 0x1b, KEY_FAVORITES}, + { 0x1d, KEY_BACK}, + { 0x4d, KEY_FASTFORWARD}, + { 0x44, KEY_EPG}, + { 0x4c, KEY_INFO}, + { 0x41, KEY_AB}, + { 0x43, KEY_AUDIO}, + { 0x45, KEY_SUBTITLE}, + { 0x4a, KEY_LIST}, + { 0x46, KEY_F1}, + { 0x47, KEY_F2}, + { 0x5e, KEY_F3}, + { 0x5c, KEY_F4}, + { 0x52, KEY_F5}, + { 0x5a, KEY_F6}, + { 0x56, KEY_MODE}, + { 0x58, KEY_SWITCHVIDEOMODE}, +}; +struct ir_scancode_table ir_codes_tevii_nec_table = { + .scan = ir_codes_tevii_nec, + .size = ARRAY_SIZE(ir_codes_tevii_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table); + static struct ir_scancode ir_codes_tbs_nec[] = { { 0x04, KEY_POWER2}, /*power*/ { 0x14, KEY_MUTE}, /*mute*/ -- cgit v1.2.1 From 760c466c66db40cde5953e93b94ae20c7610fb20 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 13 Oct 2009 23:44:14 -0300 Subject: V4L/DVB (13188): xc5000: return an error on tuning attempts if firmware not loaded Xc5000 tuning attempts shouldn't return zero in the case where the firmware did not load successfully. Thanks to Michael Krufky for pointing out this issue. Cc: Michael Krufky Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index d33bf831e76c..432003dded7c 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -633,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency); @@ -884,8 +888,12 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, if (priv->i2c_props.adap == NULL) return -EINVAL; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } switch (params->mode) { case V4L2_TUNER_RADIO: -- cgit v1.2.1 From 1724c8fa7eb33d68898e060a08a8e6a88348b62f Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 23 Oct 2009 02:47:49 -0300 Subject: V4L/DVB (13214): tda18271: allow for i2c buses that cant send 16 bytes at once There is already an option for sending 16 byte chunks rather that writing 39 bytes all at once during the tuner's initialization. Some i2c buses can't send 16 bytes at once, so create an option for sending 8 byte chunks. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-common.c | 16 ++++++++++++++-- drivers/media/common/tuners/tda18271-fe.c | 3 ++- drivers/media/common/tuners/tda18271-priv.h | 2 +- drivers/media/common/tuners/tda18271.h | 12 +++++++++--- 4 files changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index 155c93eb75da..e1f678281a58 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe) regs[R_EB22] = 0x48; regs[R_EB23] = 0xb0; - if (priv->small_i2c) { + switch (priv->small_i2c) { + case TDA18271_08_BYTE_CHUNK_INIT: + tda18271_write_regs(fe, 0x00, 0x08); + tda18271_write_regs(fe, 0x08, 0x08); + tda18271_write_regs(fe, 0x10, 0x08); + tda18271_write_regs(fe, 0x18, 0x08); + tda18271_write_regs(fe, 0x20, 0x07); + break; + case TDA18271_16_BYTE_CHUNK_INIT: tda18271_write_regs(fe, 0x00, 0x10); tda18271_write_regs(fe, 0x10, 0x10); tda18271_write_regs(fe, 0x20, 0x07); - } else + break; + case TDA18271_39_BYTE_CHUNK_INIT: + default: tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + break; + } /* setup agc1 gain */ regs[R_EB17] = 0x00; diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index e0fd9f5d5944..eb7724158861 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1224,7 +1224,8 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; priv->role = (cfg) ? cfg->role : TDA18271_MASTER; priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? cfg->small_i2c : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; priv->output_opt = (cfg) ? cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 7047680cdbd1..9589ab0576d2 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -109,10 +109,10 @@ struct tda18271_priv { enum tda18271_i2c_gate gate; enum tda18271_ver id; enum tda18271_output_options output_opt; + enum tda18271_small_i2c small_i2c; unsigned int config; /* interface to saa713x / tda829x */ unsigned int cal_initialized:1; - unsigned int small_i2c:1; u8 tm_rfcal; diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h index 323f2912128d..d7fcc36dc6e6 100644 --- a/drivers/media/common/tuners/tda18271.h +++ b/drivers/media/common/tuners/tda18271.h @@ -78,6 +78,12 @@ enum tda18271_output_options { TDA18271_OUTPUT_XT_OFF = 2, }; +enum tda18271_small_i2c { + TDA18271_39_BYTE_CHUNK_INIT = 0, + TDA18271_16_BYTE_CHUNK_INIT = 1, + TDA18271_08_BYTE_CHUNK_INIT = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -91,12 +97,12 @@ struct tda18271_config { /* output options that can be disabled */ enum tda18271_output_options output_opt; + /* some i2c providers cant write all 39 registers at once */ + enum tda18271_small_i2c small_i2c; + /* force rf tracking filter calibration on startup */ unsigned int rf_cal_on_startup:1; - /* some i2c providers cant write all 39 registers at once */ - unsigned int small_i2c:1; - /* interface to saa713x / tda829x */ unsigned int config; }; -- cgit v1.2.1 From 42f9a03ed3ddcccc5372bf4774afc8c4273c6c3a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 23 Oct 2009 03:20:45 -0300 Subject: V4L/DVB (13215): tda18271: add support for the set_config method Add the set_config method to allow drivers to configure the tda18271 driver options after attach. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 39 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index eb7724158861..ecb65802725f 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1185,6 +1185,26 @@ static int tda18271_get_id(struct dvb_frontend *fe) return ret; } +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->role = (cfg) ? cfg->role : TDA18271_MASTER; + priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + return 0; +} + static struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", @@ -1197,6 +1217,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { .set_params = tda18271_set_params, .set_analog_params = tda18271_set_analog_params, .release = tda18271_release, + .set_config = tda18271_set_config, .get_frequency = tda18271_get_frequency, .get_bandwidth = tda18271_get_bandwidth, }; @@ -1221,13 +1242,9 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, /* new tuner instance */ int rf_cal_on_startup; - priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; - priv->role = (cfg) ? cfg->role : TDA18271_MASTER; - priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? - cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; - priv->output_opt = (cfg) ? - cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + fe->tuner_priv = priv; + + tda18271_set_config(fe, cfg); /* tda18271_cal_on_startup == -1 when cal * module option is unset */ @@ -1243,8 +1260,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->cal_initialized = false; mutex_init(&priv->lock); - fe->tuner_priv = priv; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1276,14 +1291,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->small_i2c = cfg->small_i2c; if (cfg->output_opt) priv->output_opt = cfg->output_opt; + if (cfg->std_map) + tda18271_update_std_map(fe, cfg->std_map); } break; } - /* override default std map with values in config struct */ - if ((cfg) && (cfg->std_map)) - tda18271_update_std_map(fe, cfg->std_map); - mutex_unlock(&tda18271_list_mutex); memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, -- cgit v1.2.1 From c54e1dde586ea0e930d6c443ca60a9b9d0310de3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 24 Oct 2009 17:47:49 -0300 Subject: V4L/DVB (13216): tda18271: initialize tuner during set_config if rf_cal_on_startup is set If rf_cal_on_startup is enabled in the structure passed into tda18271_set_config, and the cal module option isn't disabled, then we should run the initialization and calibration procedures during the tda18271_set_config function call. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index ecb65802725f..24f1d38081e6 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1185,10 +1185,10 @@ static int tda18271_get_id(struct dvb_frontend *fe) return ret; } -static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +static int tda18271_setup_configuration(struct dvb_frontend *fe, + struct tda18271_config *cfg) { struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; priv->role = (cfg) ? cfg->role : TDA18271_MASTER; @@ -1205,6 +1205,28 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) return 0; } +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + int rf_cal_on_startup; + + tda18271_setup_configuration(fe, cfg); + + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + if (tda18271_cal_on_startup == -1) { + /* honor configuration setting */ + rf_cal_on_startup = + ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; + } else { + /* module option overrides configuration setting */ + rf_cal_on_startup = tda18271_cal_on_startup; + } + if (rf_cal_on_startup) + tda18271_init(fe); + + return 0; +} + static struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", @@ -1244,7 +1266,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, fe->tuner_priv = priv; - tda18271_set_config(fe, cfg); + tda18271_setup_configuration(fe, cfg); /* tda18271_cal_on_startup == -1 when cal * module option is unset */ -- cgit v1.2.1 From 188ea058ff2e671270273a1f5d13f46ca75b8a35 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 24 Oct 2009 18:18:03 -0300 Subject: V4L/DVB (13217): tda18271: handle rf_cal_on_startup properly during attach If rf_cal_on_startup is enabled in the structure passed into tda18271_attach, and the cal module option isn't disabled, then we should run the initialization and calibration procedures during the tda18271_attach function call, regardless of how many instances of the driver have been attached. If the device is already calibrated, the driver will detect that and will only calibrate again if necessary. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 24f1d38081e6..8b934f48b6f3 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1249,7 +1249,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct tda18271_config *cfg) { struct tda18271_priv *priv = NULL; - int instance; + int instance, rf_cal_on_startup = 0; mutex_lock(&tda18271_list_mutex); @@ -1262,8 +1262,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, case 1: { /* new tuner instance */ - int rf_cal_on_startup; - fe->tuner_priv = priv; tda18271_setup_configuration(fe, cfg); @@ -1315,7 +1313,20 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->output_opt = cfg->output_opt; if (cfg->std_map) tda18271_update_std_map(fe, cfg->std_map); + + /* tda18271_cal_on_startup == -1 when cal + * module option is unset */ + if (tda18271_cal_on_startup == -1) { + /* honor attach-time configuration */ + rf_cal_on_startup = + (cfg->rf_cal_on_startup) ? 1 : 0; + } else { + /* module option overrides attach config */ + rf_cal_on_startup = tda18271_cal_on_startup; + } } + if (rf_cal_on_startup) + tda18271_init(fe); break; } -- cgit v1.2.1 From 5881ecfca058d731758c76018205b5b3dff9291f Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 24 Oct 2009 18:37:22 -0300 Subject: V4L/DVB (13218): tda18271: consolidate common code for calibration on startup determination consolidate common code used to determine if calibration on startup is needed Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 51 +++++++++---------------------- 1 file changed, 15 insertions(+), 36 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 8b934f48b6f3..cae0df1748f1 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1205,23 +1205,23 @@ static int tda18271_setup_configuration(struct dvb_frontend *fe, return 0; } +static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +{ + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + return ((tda18271_cal_on_startup == -1) ? + /* honor configuration setting */ + ((cfg) && (cfg->rf_cal_on_startup)) : + /* module option overrides configuration setting */ + (tda18271_cal_on_startup)) ? 1 : 0; +} + static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) { struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; - int rf_cal_on_startup; tda18271_setup_configuration(fe, cfg); - /* tda18271_cal_on_startup == -1 when cal module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor configuration setting */ - rf_cal_on_startup = - ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; - } else { - /* module option overrides configuration setting */ - rf_cal_on_startup = tda18271_cal_on_startup; - } - if (rf_cal_on_startup) + if (tda18271_need_cal_on_startup(cfg)) tda18271_init(fe); return 0; @@ -1249,7 +1249,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct tda18271_config *cfg) { struct tda18271_priv *priv = NULL; - int instance, rf_cal_on_startup = 0; + int instance; mutex_lock(&tda18271_list_mutex); @@ -1266,17 +1266,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, tda18271_setup_configuration(fe, cfg); - /* tda18271_cal_on_startup == -1 when cal - * module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor attach-time configuration */ - rf_cal_on_startup = - ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; - } else { - /* module option overrides attach configuration */ - rf_cal_on_startup = tda18271_cal_on_startup; - } - priv->cal_initialized = false; mutex_init(&priv->lock); @@ -1289,7 +1278,8 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_lock(&priv->lock); tda18271_init_regs(fe); - if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2)) + if ((tda18271_need_cal_on_startup(cfg)) && + (priv->id == TDA18271HDC2)) tda18271c2_rf_cal_init(fe); mutex_unlock(&priv->lock); @@ -1313,19 +1303,8 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->output_opt = cfg->output_opt; if (cfg->std_map) tda18271_update_std_map(fe, cfg->std_map); - - /* tda18271_cal_on_startup == -1 when cal - * module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor attach-time configuration */ - rf_cal_on_startup = - (cfg->rf_cal_on_startup) ? 1 : 0; - } else { - /* module option overrides attach config */ - rf_cal_on_startup = tda18271_cal_on_startup; - } } - if (rf_cal_on_startup) + if (tda18271_need_cal_on_startup(cfg)) tda18271_init(fe); break; } -- cgit v1.2.1 From 712bc0b30fa72edd1b3910250dcaf52894871714 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 25 Oct 2009 12:36:26 -0300 Subject: V4L/DVB (13219): tda18271: remove unneeded braces in switch..case block Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index cae0df1748f1..9199e362279c 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1260,7 +1260,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, case 0: goto fail; case 1: - { /* new tuner instance */ fe->tuner_priv = priv; @@ -1284,7 +1283,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_unlock(&priv->lock); break; - } default: /* existing tuner instance */ fe->tuner_priv = priv; -- cgit v1.2.1 From 5abaa530bfe10e2a446679442b283f865a444a32 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 25 Oct 2009 10:43:30 -0300 Subject: V4L/DVB (13220): tda18271: increment module version minor Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 9199e362279c..07d6e1c159ca 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1326,7 +1326,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach); MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); +MODULE_VERSION("0.4"); /* * Overrides for Emacs so that we follow Linus's tabbing style. -- cgit v1.2.1 From 311c70e1f906b7411b30f526ef15deb62cb37e7a Mon Sep 17 00:00:00 2001 From: Johann Friedrichs Date: Wed, 7 Oct 2009 04:41:37 -0300 Subject: V4L/DVB (13239): saa7146: fix memory leakage in pagetable-handling In buffer_release() the previously allocated pagetables are not freed, which might result in a memory leak in certain application use-cases, where the frame format is changed from planar format to non-planar format. The fix explicitely frees the page tables when a format change is done and when buffer_release() is called. Signed-off-by: Johann Friedrichs Signed-off-by: Michael Hunold Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_video.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 552dab442d78..becbaadb3b77 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1205,6 +1205,13 @@ static int buffer_activate (struct saa7146_dev *dev, return 0; } +static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + saa7146_pgtable_free(dev->pci, &buf->pt[0]); + saa7146_pgtable_free(dev->pci, &buf->pt[1]); + saa7146_pgtable_free(dev->pci, &buf->pt[2]); +} + static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -1257,16 +1264,12 @@ static int buffer_prepare(struct videobuf_queue *q, sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); + release_all_pagetables(dev, buf); if( 0 != IS_PLANAR(sfmt->trans)) { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); - saa7146_pgtable_free(dev->pci, &buf->pt[1]); - saa7146_pgtable_free(dev->pci, &buf->pt[2]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); } else { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); } @@ -1329,6 +1332,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP(("vbuf:%p\n",vb)); + + release_all_pagetables(dev, buf); + saa7146_dma_free(dev,q,buf); } -- cgit v1.2.1 From f14a2972e40dbfbe7077ec7ab21cc2729f7e7d6d Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 23 Oct 2009 07:59:42 -0300 Subject: V4L/DVB (13241): Cleanup redundant tests on unsigned The variables are unsigned so the test `>= 0' is always true, the `< 0' test always fails. In these cases the other part of the test catches wrapped values. Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda9887.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index 544cdbe88a6c..a71c100c95df 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe) buf[1] &= ~cQSS; } - if (adjust >= 0x00 && adjust < 0x20) { + if (adjust < 0x20) { buf[2] &= ~cTopMask; buf[2] |= adjust; } -- cgit v1.2.1 From 0700adec9efec3136fa44d90f064c9227817f37b Mon Sep 17 00:00:00 2001 From: Michael Obst Date: Sat, 31 Oct 2009 14:05:42 -0300 Subject: V4L/DVB (13260): saa7134: add support for Leadtek Winfast DTV-1000S remote control Signed-off-by: Michael Obst Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 32f765e6e5fd..e353dffe696a 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1705,6 +1705,7 @@ static struct ir_scancode ir_codes_winfast[] = { { 0x37, KEY_RADIO }, /* FM */ { 0x38, KEY_DVD }, + { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ -- cgit v1.2.1 From 972aacc2989506d9c67f8b710021ff98041363d5 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 1 Nov 2009 02:52:01 -0300 Subject: V4L/DVB (13262): tda18271: fix regression preventing std map override from taking effect In the changeset entitled, "tda18271: add support for the set_config method", the initialization order was changed such that the standard map overrides were being applied before loading the default standard map, based on the stepping of the silicon. This changeset restores the proper order by processing the standard map overrides both within the tda18271_set_config() and the tda18271_attach() functions, after we read the id register of the tuner. Thanks to Michael Obst for his testing, helping me to identify the regression. Signed-off-by: Michael Krufky Signed-off-by: Michael Obst Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda18271-fe.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 07d6e1c159ca..b2e15456d5f3 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1198,10 +1198,6 @@ static int tda18271_setup_configuration(struct dvb_frontend *fe, priv->output_opt = (cfg) ? cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; - /* override default std map with values in config struct */ - if ((cfg) && (cfg->std_map)) - tda18271_update_std_map(fe, cfg->std_map); - return 0; } @@ -1224,6 +1220,10 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) if (tda18271_need_cal_on_startup(cfg)) tda18271_init(fe); + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + return 0; } @@ -1307,6 +1307,10 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, break; } + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + mutex_unlock(&tda18271_list_mutex); memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, -- cgit v1.2.1 From 48c511ed675772bd08044d97756f7f7aae5168eb Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 28 Oct 2009 23:10:16 -0300 Subject: V4L/DVB (13331): mxl5005s: provide ability to override QAM gain for HVR-1600 The HVR-1600 doesn't use the standard mechanism for computing the gain when in QAM mode, instead always forcing it to be 0x02. Provide the ability to override the algorithm on a per-board basis. This change results in an improvement of 0.4-0.6 dB for QAM tuning. This work was sponsored by ONELAN Limited. Cc: Steven Toth Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/mxl5005s.c | 5 ++++- drivers/media/common/tuners/mxl5005s.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c index 0803dab58fff..605e28b73263 100644 --- a/drivers/media/common/tuners/mxl5005s.c +++ b/drivers/media/common/tuners/mxl5005s.c @@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) /* add for 2.6.5 Special setting for QAM */ if (state->Mod_Type == MXL_QAM) { - if (state->RF_IN < 680000000) + if (state->config->qam_gain != 0) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, + state->config->qam_gain); + else if (state->RF_IN < 680000000) status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); else status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h index 7ac6815b30aa..fc8a1ffc53b4 100644 --- a/drivers/media/common/tuners/mxl5005s.h +++ b/drivers/media/common/tuners/mxl5005s.h @@ -108,6 +108,10 @@ struct mxl5005s_config { #define MXL_LOW_IF 1 u8 if_mode; + /* Some boards need to override the built-in logic for determining + the gain when in QAM mode (the HVR-1600 is one such case) */ + u8 qam_gain; + /* Stuff I don't know what to do with */ u8 AgcMasterByte; }; -- cgit v1.2.1 From 95963cbcd0ee00026cc34ba84906ee4aa2029f4d Mon Sep 17 00:00:00 2001 From: "David T.L. Wong" Date: Mon, 26 Oct 2009 06:42:34 -0300 Subject: V4L/DVB (13373): Maxium MAX2165 silicon tuner Adds support for Maxim MAX2165 silicon tuner. It is tested on Mygica X8558Pro, which has MAX2165, ATBM8830 and CX23885 Signed-off-by: David T. L. Wong Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/Kconfig | 7 + drivers/media/common/tuners/Makefile | 1 + drivers/media/common/tuners/max2165.c | 442 +++++++++++++++++++++++++++++ drivers/media/common/tuners/max2165.h | 48 ++++ drivers/media/common/tuners/max2165_priv.h | 60 ++++ 5 files changed, 558 insertions(+) create mode 100644 drivers/media/common/tuners/max2165.c create mode 100644 drivers/media/common/tuners/max2165.h create mode 100644 drivers/media/common/tuners/max2165_priv.h (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 607d319ce8ed..409a4261e5b5 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803 help Say Y here to support the Freescale MC44S803 based tuners +config MEDIA_TUNER_MAX2165 + tristate "Maxim MAX2165 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + A driver for the silicon tuner MAX2165 from Maxim. + endif # MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 4132b2be79e5..a5438523f30d 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c new file mode 100644 index 000000000000..1b486cfb8ed9 --- /dev/null +++ b/drivers/media/common/tuners/max2165.c @@ -0,0 +1,442 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "max2165.h" +#include "max2165_priv.h" +#include "tuner-i2c.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "max2165: " args); \ + } while (0) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->i2c_address; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr = priv->config->i2c_address; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + *p_data = b1[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, b1[0]); + return 0; +} + +static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 v; + + data &= mask; + ret = max2165_read_reg(priv, reg, &v); + if (ret != 0) + return ret; + v &= ~mask; + v |= data; + ret = max2165_write_reg(priv, reg, v); + + return ret; +} + +static int max2165_read_rom_table(struct max2165_priv *priv) +{ + u8 dat[3]; + int i; + + for (i = 0; i < 3; i++) { + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); + max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); + } + + priv->tf_ntch_low_cfg = dat[0] >> 4; + priv->tf_ntch_hi_cfg = dat[0] & 0x0F; + priv->tf_balun_low_ref = dat[1] & 0x0F; + priv->tf_balun_hi_ref = dat[1] >> 4; + priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; + priv->bb_filter_8mhz_cfg = dat[2] >> 4; + + dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); + dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); + dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); + dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); + dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); + dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); + + return 0; +} + +static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +{ + u8 v; + + v = (osc / 2); + if (v == 2) + v = 0x7; + else + v -= 8; + + max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); + + return 0; +} + +static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +{ + u8 val; + + if (bw == BANDWIDTH_8_MHZ) + val = priv->bb_filter_8mhz_cfg; + else + val = priv->bb_filter_7mhz_cfg; + + max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); + + return 0; +} + +int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +{ + u32 remainder; + u32 q, f = 0; + int i; + + if (0 == divisor) + return -1; + + q = dividend / divisor; + remainder = dividend - q * divisor; + + for (i = 0; i < 31; i++) { + remainder <<= 1; + if (remainder >= divisor) { + f += 1; + remainder -= divisor; + } + f <<= 1; + } + + *quotient = q; + *fraction = f; + + return 0; +} + +static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +{ + u8 tf; + u8 tf_ntch; + double t; + u32 quotient, fraction; + + /* Set PLL divider according to RF frequency */ + fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + + /* 20-bit fraction */ + fraction >>= 12; + + max2165_write_reg(priv, REG_NDIV_INT, quotient); + max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); + max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); + max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); + + /* Norch Filter */ + tf_ntch = (freq < 725000000) ? + priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; + + /* Tracking filter balun */ + t = priv->tf_balun_low_ref; + t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) + * (freq / 1000 - 470000) / (780000 - 470000); + + tf = t; + dprintk("tf = %X\n", tf); + tf |= tf_ntch << 4; + + max2165_write_reg(priv, REG_TRACK_FILTER, tf); + + return 0; +} + +static void max2165_debug_status(struct max2165_priv *priv) +{ + u8 status, autotune; + u8 auto_vco_success, auto_vco_active; + u8 pll_locked; + u8 dc_offset_low, dc_offset_hi; + u8 signal_lv_over_threshold; + u8 vco, vco_sub_band, adc; + + max2165_read_reg(priv, REG_STATUS, &status); + max2165_read_reg(priv, REG_AUTOTUNE, &autotune); + + auto_vco_success = (status >> 6) & 0x01; + auto_vco_active = (status >> 5) & 0x01; + pll_locked = (status >> 4) & 0x01; + dc_offset_low = (status >> 3) & 0x01; + dc_offset_hi = (status >> 2) & 0x01; + signal_lv_over_threshold = status & 0x01; + + vco = autotune >> 6; + vco_sub_band = (autotune >> 3) & 0x7; + adc = autotune & 0x7; + + dprintk("auto VCO active: %d, auto VCO success: %d\n", + auto_vco_active, auto_vco_success); + dprintk("PLL locked: %d\n", pll_locked); + dprintk("DC offset low: %d, DC offset high: %d\n", + dc_offset_low, dc_offset_hi); + dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); + dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +} + +static int max2165_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct max2165_priv *priv = fe->tuner_priv; + int ret; + + dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency); + if (fe->ops.info.type == FE_ATSC) { + return -EINVAL; + } else if (fe->ops.info.type == FE_OFDM) { + dprintk("%s() OFDM\n", __func__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + return -EINVAL; + case BANDWIDTH_7_MHZ: + case BANDWIDTH_8_MHZ: + priv->frequency = params->frequency; + priv->bandwidth = params->u.ofdm.bandwidth; + break; + default: + printk(KERN_ERR "MAX2165 bandwidth not set!\n"); + return -EINVAL; + } + } else { + printk(KERN_ERR "MAX2165 modulation type not supported!\n"); + return -EINVAL; + } + + dprintk("%s() frequency=%d\n", __func__, priv->frequency); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + max2165_set_bandwidth(priv, priv->bandwidth); + ret = max2165_set_rf(priv, priv->frequency); + mdelay(50); + max2165_debug_status(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 0) + return -EREMOTEIO; + + return 0; +} + +static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + *freq = priv->frequency; + return 0; +} + +static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct max2165_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + max2165_debug_status(priv); + *status = lock_status; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static int max2165_init(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Setup initial values */ + /* Fractional Mode on */ + max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); + /* LNA on */ + max2165_write_reg(priv, REG_LNA, 0x01); + max2165_write_reg(priv, REG_PLL_CFG, 0x7A); + max2165_write_reg(priv, REG_TEST, 0x08); + max2165_write_reg(priv, REG_SHUTDOWN, 0x40); + max2165_write_reg(priv, REG_VCO_CTRL, 0x84); + max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); + max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); + max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); + + max2165_set_osc(priv, priv->config->osc_clk); + + max2165_read_rom_table(priv); + + max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_release(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + kfree(priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops max2165_tuner_ops = { + .info = { + .name = "Maxim MAX2165", + .frequency_min = 470000000, + .frequency_max = 780000000, + .frequency_step = 50000, + }, + + .release = max2165_release, + .init = max2165_init, + .sleep = max2165_sleep, + + .set_params = max2165_set_params, + .set_analog_params = NULL, + .get_frequency = max2165_get_frequency, + .get_bandwidth = max2165_get_bandwidth, + .get_status = max2165_get_status +}; + +struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + struct max2165_priv *priv = NULL; + + dprintk("%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + priv->config = cfg; + priv->i2c = i2c; + fe->tuner_priv = priv; + + max2165_init(fe); + max2165_debug_status(priv); + + return fe; +} +EXPORT_SYMBOL(max2165_attach); + +MODULE_AUTHOR("David T. L. Wong "); +MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h new file mode 100644 index 000000000000..c063c36a93d3 --- /dev/null +++ b/drivers/media/common/tuners/max2165.h @@ -0,0 +1,48 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_H__ +#define __MAX2165_H__ + +struct dvb_frontend; +struct i2c_adapter; + +struct max2165_config { + u8 i2c_address; + u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ +}; + +#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ + (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg); +#else +static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h new file mode 100644 index 000000000000..91bbe021a08d --- /dev/null +++ b/drivers/media/common/tuners/max2165_priv.h @@ -0,0 +1,60 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_PRIV_H__ +#define __MAX2165_PRIV_H__ + +#define REG_NDIV_INT 0x00 +#define REG_NDIV_FRAC2 0x01 +#define REG_NDIV_FRAC1 0x02 +#define REG_NDIV_FRAC0 0x03 +#define REG_TRACK_FILTER 0x04 +#define REG_LNA 0x05 +#define REG_PLL_CFG 0x06 +#define REG_TEST 0x07 +#define REG_SHUTDOWN 0x08 +#define REG_VCO_CTRL 0x09 +#define REG_BASEBAND_CTRL 0x0A +#define REG_DC_OFFSET_CTRL 0x0B +#define REG_DC_OFFSET_DAC 0x0C +#define REG_ROM_TABLE_ADDR 0x0D + +/* Read Only Registers */ +#define REG_ROM_TABLE_DATA 0x10 +#define REG_STATUS 0x11 +#define REG_AUTOTUNE 0x12 + +struct max2165_priv { + struct max2165_config *config; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + + u8 tf_ntch_low_cfg; + u8 tf_ntch_hi_cfg; + u8 tf_balun_low_ref; + u8 tf_balun_hi_ref; + u8 bb_filter_7mhz_cfg; + u8 bb_filter_8mhz_cfg; +}; + +#endif -- cgit v1.2.1 From c95a419a5604ec8a23cd73f61e9bb151e8cbe89b Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 20 Nov 2009 15:34:13 -0300 Subject: V4L/DVB: Fix test in copy_reg_bits() The reg_pair2[j].reg was tested twice. Cc: Signed-off-by: Roel Kluin Acked-by: Michael Krufky Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/mxl5007t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c index 2d02698d4f4f..7eb1bf75cd07 100644 --- a/drivers/media/common/tuners/mxl5007t.c +++ b/drivers/media/common/tuners/mxl5007t.c @@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1, i = j = 0; while (reg_pair1[i].reg || reg_pair1[i].val) { - while (reg_pair2[j].reg || reg_pair2[j].reg) { + while (reg_pair2[j].reg || reg_pair2[j].val) { if (reg_pair1[i].reg != reg_pair2[j].reg) { j++; continue; -- cgit v1.2.1 From 4e89217b943cfb26f88f04920d44f2077931f0e7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Nov 2009 21:54:41 -0300 Subject: V4L/DVB (13531): ir-common: rename the debug routine to allow exporting it As newer IR common code will be added on other files, we need a global debug var inside the module. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-functions.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index fc2d8941459f..b111a0d9409b 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -34,11 +34,8 @@ static int repeat = 1; module_param(repeat, int, 0444); MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)"); -static int debug; /* debug level (0,1,2) */ -module_param(debug, int, 0644); - -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG fmt , ## arg) +int media_ir_debug; /* media_ir_debug level (0,1,2) */ +module_param_named(debug, media_ir_debug, int, 0644); /* -------------------------------------------------------------------------- */ @@ -49,7 +46,7 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) dev->name,ir->ir_key,ir->ir_raw,ir->keypressed); return; } - dprintk(1,"%s: key event code=%d down=%d\n", + IR_dprintk(1,"%s: key event code=%d down=%d\n", dev->name,ir->keycode,ir->keypressed); input_report_key(dev,ir->keycode,ir->keypressed); input_sync(dev); @@ -295,11 +292,11 @@ u32 ir_rc5_decode(unsigned int code) rc5 |= 1; break; case 3: - dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); + IR_dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); return 0; } } - dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + IR_dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " "instr=%x\n", rc5, org_code, RC5_START(rc5), RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5; @@ -331,20 +328,20 @@ void ir_rc5_timer_end(unsigned long data) /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ if (gap < 28000) { - dprintk(1, "ir-common: spurious timer_end\n"); + IR_dprintk(1, "ir-common: spurious timer_end\n"); return; } if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ - dprintk(1, "ir-common: short code: %x\n", ir->code); + IR_dprintk(1, "ir-common: short code: %x\n", ir->code); } else { ir->code = (ir->code << ir->shift_by) | 1; rc5 = ir_rc5_decode(ir->code); /* two start bits? */ if (RC5_START(rc5) != ir->start) { - dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); + IR_dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); /* right address? */ } else if (RC5_ADDR(rc5) == ir->addr) { @@ -354,7 +351,7 @@ void ir_rc5_timer_end(unsigned long data) /* Good code, decide if repeat/repress */ if (toggle != RC5_TOGGLE(ir->last_rc5) || instr != RC5_INSTR(ir->last_rc5)) { - dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, + IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, toggle); ir_input_nokey(ir->dev, &ir->ir); ir_input_keydown(ir->dev, &ir->ir, instr, @@ -377,7 +374,7 @@ void ir_rc5_timer_keyup(unsigned long data) { struct card_ir *ir = (struct card_ir *)data; - dprintk(1, "ir-common: key released\n"); + IR_dprintk(1, "ir-common: key released\n"); ir_input_nokey(ir->dev, &ir->ir); } EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup); -- cgit v1.2.1 From ef53a1159dfcdc1fecf5adb5b8d26803f194c09b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Nov 2009 22:01:23 -0300 Subject: V4L/DVB (13532): ir-common: Add infrastructure to use a dynamic keycode table V4L drivers use an static keycode vector with 128 entries, where the scancode indexes the keycode. While this works, it limits the scancodes to have only 7 bits, not allowing for example full RC5 codes. Instead of implementing the same code on every V4L driver, provide a common infrastructure to handle the bigger tables, minimizing the changes inside each driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Makefile | 2 +- drivers/media/common/ir-keytable.c | 156 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 drivers/media/common/ir-keytable.c (limited to 'drivers/media/common') diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 351b98b9b302..169b337b7c9d 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,6 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o -ir-common-objs := ir-functions.o ir-keymaps.o +ir-common-objs := ir-functions.o ir-keymaps.o ir-keytable.o obj-y += tuners/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c new file mode 100644 index 000000000000..2de7caeef437 --- /dev/null +++ b/drivers/media/common/ir-keytable.c @@ -0,0 +1,156 @@ +/* ir-register.c - handle IR scancode->keycode tables + * + * Copyright (C) 2009 by Mauro Carvalho Chehab + */ + +#include + +#include + +/** + * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCGKEY ioctl. + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + int i; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + + /* See if we can match the raw key code. */ + for (i = 0; i < rc_tab->size; i++) + if (keymap[i].scancode == scancode) { + *keycode = keymap[i].keycode; + return 0; + } + + /* + * If is there extra space, returns KEY_RESERVED, + * otherwise, input core won't let ir_setkeycode + * to work + */ + for (i = 0; i < rc_tab->size; i++) + if (keymap[i].keycode == KEY_RESERVED || + keymap[i].keycode == KEY_UNKNOWN) { + *keycode = KEY_RESERVED; + return 0; + } + + return -EINVAL; +} + +/** + * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCSKEY ioctl. + * There's one caveat here: how can we increase the size of the table? + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + int i; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + + /* Search if it is replacing an existing keycode */ + for (i = 0; i < rc_tab->size; i++) + if (keymap[i].scancode == scancode) { + keymap[i].keycode = keycode; + return 0; + } + + /* Search if is there a clean entry. If so, use it */ + for (i = 0; i < rc_tab->size; i++) + if (keymap[i].keycode == KEY_RESERVED || + keymap[i].keycode == KEY_UNKNOWN) { + keymap[i].scancode = scancode; + keymap[i].keycode = keycode; + return 0; + } + + /* + * FIXME: Currently, it is not possible to increase the size of + * scancode table. For it to happen, one possibility + * would be to allocate a table with key_map_size + 1, + * copying data, appending the new key on it, and freeing + * the old one - or maybe just allocating some spare space + */ + + return -EINVAL; +} + +/** + * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode + * @rc_tab: the ir_scancode_table with the keymap to be used + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) +{ + int i; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + + for (i = 0; i < rc_tab->size; i++) + if (keymap[i].scancode == scancode) { + IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->name, scancode, keymap[i].keycode); + + return keymap[i].keycode; + } + + printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n", + dev->name, scancode); + + return KEY_UNKNOWN; +} +EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); + +/** + * ir_set_keycode_table() - sets the IR keycode table and add the handlers + * for keymap table get/set + * @input_dev: the struct input_dev descriptor of the device + * @rc_tab: the struct ir_scancode_table table of scancode/keymap + * + * This routine is used to initialize the input infrastructure to work with + * an IR. It requires that the caller initializes the input_dev struct with + * some fields: name, + */ +int ir_set_keycode_table(struct input_dev *input_dev, + struct ir_scancode_table *rc_tab) +{ + struct ir_scancode *keymap = rc_tab->scan; + int i; + + if (rc_tab->scan == NULL || !rc_tab->size) + return -EINVAL; + + /* set the bits for the keys */ + IR_dprintk(1, "key map size: %d\n", rc_tab->size); + for (i = 0; i < rc_tab->size; i++) { + IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n", + i, keymap[i].keycode); + set_bit(keymap[i].keycode, input_dev->keybit); + } + + input_dev->getkeycode = ir_getkeycode; + input_dev->setkeycode = ir_setkeycode; + input_set_drvdata(input_dev, rc_tab); + + return 0; +} +EXPORT_SYMBOL_GPL(ir_set_keycode_table); -- cgit v1.2.1 From 8573b74af25c279de3e309beddcba984bee9ec15 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Nov 2009 22:40:22 -0300 Subject: V4L/DVB (13533): ir: use dynamic tables, instead of static ones Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-functions.c | 32 ++++++-------------------------- drivers/media/common/ir-keytable.c | 2 -- 2 files changed, 6 insertions(+), 28 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index b111a0d9409b..29885c2893d2 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -42,8 +42,8 @@ module_param_named(debug, media_ir_debug, int, 0644); static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) { if (KEY_RESERVED == ir->keycode) { - printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n", - dev->name,ir->ir_key,ir->ir_raw,ir->keypressed); + printk(KERN_INFO "%s: unknown key: key=0x%02x down=%d\n", + dev->name, ir->ir_key, ir->keypressed); return; } IR_dprintk(1,"%s: key event code=%d down=%d\n", @@ -57,28 +57,10 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, struct ir_scancode_table *ir_codes) { - int i; - ir->ir_type = ir_type; - memset(ir->ir_codes, 0, sizeof(ir->ir_codes)); - - /* - * FIXME: This is a temporary workaround to use the new IR tables - * with the old approach. Later patches will replace this to a - * proper method - */ - - if (ir_codes) - for (i = 0; i < ir_codes->size; i++) - if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE) - ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode; + ir_set_keycode_table(dev, ir_codes); - dev->keycode = ir->ir_codes; - dev->keycodesize = sizeof(IR_KEYTAB_TYPE); - dev->keycodemax = IR_KEYTAB_SIZE; - for (i = 0; i < IR_KEYTAB_SIZE; i++) - set_bit(ir->ir_codes[i], dev->keybit); clear_bit(0, dev->keybit); set_bit(EV_KEY, dev->evbit); @@ -97,9 +79,9 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) EXPORT_SYMBOL_GPL(ir_input_nokey); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, - u32 ir_key, u32 ir_raw) + u32 ir_key) { - u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key); + u32 keycode = ir_g_keycode_from_table(dev, ir_key); if (ir->keypressed && ir->keycode != keycode) { ir->keypressed = 0; @@ -107,7 +89,6 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, } if (!ir->keypressed) { ir->ir_key = ir_key; - ir->ir_raw = ir_raw; ir->keycode = keycode; ir->keypressed = 1; ir_input_key_event(dev,ir); @@ -354,8 +335,7 @@ void ir_rc5_timer_end(unsigned long data) IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, toggle); ir_input_nokey(ir->dev, &ir->ir); - ir_input_keydown(ir->dev, &ir->ir, instr, - instr); + ir_input_keydown(ir->dev, &ir->ir, instr); } /* Set/reset key-up timer */ diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c index 2de7caeef437..65e352389b2d 100644 --- a/drivers/media/common/ir-keytable.c +++ b/drivers/media/common/ir-keytable.c @@ -118,7 +118,6 @@ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) return KEY_UNKNOWN; } -EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); /** * ir_set_keycode_table() - sets the IR keycode table and add the handlers @@ -153,4 +152,3 @@ int ir_set_keycode_table(struct input_dev *input_dev, return 0; } -EXPORT_SYMBOL_GPL(ir_set_keycode_table); -- cgit v1.2.1 From 35d1988c6e19db3d4240e2a60c71b3a13abf0781 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 27 Nov 2009 23:25:13 -0300 Subject: V4L/DVB (13535): ir-common: Add a hauppauge new table with the complete RC5 code Now that V4L drivers can support more than 7 bits for scan code, let's add a modified version for the Hauppauge Grey IR containing the full IR scancode. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index e353dffe696a..328c973a0838 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1847,6 +1847,76 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = { }; EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table); +/* + * Hauppauge:the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * + * This table contains the complete RC5 code, instead of just the data part + */ +static struct ir_scancode ir_codes_rc5_hauppauge_new[] = { + /* Keys 0 to 9 */ + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + + { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x1e0b, KEY_RED }, /* red button */ + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + /* 0x1e1a: Pictures - presume this means + "Multimedia Home Platform" - + no "PICTURES" key in input.h + */ + { 0x1e1a, KEY_MHP }, + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e25, KEY_ENTER }, /* OK */ + { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x1e29, KEY_BLUE }, /* blue key */ + { 0x1e2e, KEY_GREEN }, /* green button */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e38, KEY_YELLOW }, /* yellow key */ + { 0x1e3b, KEY_SELECT }, /* top right button */ + { 0x1e3c, KEY_ZOOM }, /* full */ + { 0x1e3d, KEY_POWER }, /* system power (green button) */ +}; + +struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = { + .scan = ir_codes_rc5_hauppauge_new, + .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new), +}; +EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table); + static struct ir_scancode ir_codes_npgtech[] = { { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ { 0x2a, KEY_FRONT }, @@ -3243,3 +3313,4 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = { .size = ARRAY_SIZE(ir_codes_gadmei_rm008z), }; EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table); + -- cgit v1.2.1 From 055cd55601f948675006ca90362fc2bfaae90a86 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 29 Nov 2009 08:19:59 -0300 Subject: V4L/DVB (13537): ir: Prepare the code for dynamic keycode table allocation Currently, the IR table is initialized by calling ir_input_init(). However, this function doesn't return any error code, nor has a function to be called when de-initializing the IR's. Change the return argment to integer and make sure that each driver will handle the error code. Also adds a function to free any resources that may be allocating there: ir_input_free(). Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-functions.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index 29885c2893d2..b31bd27da374 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -54,11 +54,13 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) /* -------------------------------------------------------------------------- */ -void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, +int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, struct ir_scancode_table *ir_codes) { ir->ir_type = ir_type; + /* FIXME: Add the proper code to dynamically allocate IR table */ + ir_set_keycode_table(dev, ir_codes); clear_bit(0, dev->keybit); @@ -66,9 +68,17 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, set_bit(EV_KEY, dev->evbit); if (repeat) set_bit(EV_REP, dev->evbit); + + return 0; } EXPORT_SYMBOL_GPL(ir_input_init); +void ir_input_free(struct input_dev *input_dev) +{ + /* FIXME: Add the proper code to free allocated resources */ +} +EXPORT_SYMBOL_GPL(ir_input_free); + void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) { if (ir->keypressed) { -- cgit v1.2.1 From f6fc50494027e913ff0159e43c593cd75f35ec7a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 29 Nov 2009 11:08:02 -0300 Subject: V4L/DVB (13538): ir-common: Use a dynamic keycode table Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-functions.c | 19 ++++++---- drivers/media/common/ir-keytable.c | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index b31bd27da374..e616f624ceaa 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -59,12 +59,20 @@ int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, { ir->ir_type = ir_type; - /* FIXME: Add the proper code to dynamically allocate IR table */ + ir->keytable.size = ir_roundup_tablesize(ir_codes->size); + ir->keytable.scan = kzalloc(ir->keytable.size * + sizeof(struct ir_scancode), GFP_KERNEL); + if (!ir->keytable.scan) + return -ENOMEM; - ir_set_keycode_table(dev, ir_codes); + IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n", + ir->keytable.size, + ir->keytable.size * sizeof(ir->keytable.scan)); - clear_bit(0, dev->keybit); + ir_copy_table(&ir->keytable, ir_codes); + ir_set_keycode_table(dev, &ir->keytable); + clear_bit(0, dev->keybit); set_bit(EV_KEY, dev->evbit); if (repeat) set_bit(EV_REP, dev->evbit); @@ -73,11 +81,6 @@ int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, } EXPORT_SYMBOL_GPL(ir_input_init); -void ir_input_free(struct input_dev *input_dev) -{ - /* FIXME: Add the proper code to free allocated resources */ -} -EXPORT_SYMBOL_GPL(ir_input_free); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) { diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c index 65e352389b2d..6e3cc78c33b2 100644 --- a/drivers/media/common/ir-keytable.c +++ b/drivers/media/common/ir-keytable.c @@ -7,6 +7,68 @@ #include +#define IR_TAB_MIN_SIZE 32 + +/** + * ir_roundup_tablesize() - gets an optimum value for the table size + * @n_elems: minimum number of entries to store keycodes + * + * This routine is used to choose the keycode table size. + * + * In order to have some empty space for new keycodes, + * and knowing in advance that kmalloc allocates only power of two + * segments, it optimizes the allocated space to have some spare space + * for those new keycodes by using the maximum number of entries that + * will be effectively be allocated by kmalloc. + * In order to reduce the quantity of table resizes, it has a minimum + * table size of IR_TAB_MIN_SIZE. + */ +int ir_roundup_tablesize(int n_elems) +{ + size_t size; + + if (n_elems < IR_TAB_MIN_SIZE) + n_elems = IR_TAB_MIN_SIZE; + + /* + * As kmalloc only allocates sizes of power of two, get as + * much entries as possible for the allocated memory segment + */ + size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode)); + n_elems = size / sizeof(struct ir_scancode); + + return n_elems; +} + +/** + * ir_copy_table() - copies a keytable, discarding the unused entries + * @destin: destin table + * @origin: origin table + * + * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED + */ + +int ir_copy_table(struct ir_scancode_table *destin, + const struct ir_scancode_table *origin) +{ + int i, j = 0; + + for (i = 0; i < origin->size; i++) { + if (origin->scan[i].keycode != KEY_UNKNOWN && + origin->scan[i].keycode != KEY_RESERVED) { + memcpy(&destin->scan[j], &origin->scan[i], + sizeof(struct ir_scancode)); + j++; + } + } + destin->size = j; + + IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", j); + + return 0; +} + + /** * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table * @dev: the struct input_dev device descriptor @@ -152,3 +214,16 @@ int ir_set_keycode_table(struct input_dev *input_dev, return 0; } + +void ir_input_free(struct input_dev *dev) +{ + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + + IR_dprintk(1, "Freed keycode table\n"); + + rc_tab->size = 0; + kfree(rc_tab->scan); + rc_tab->scan = NULL; +} +EXPORT_SYMBOL_GPL(ir_input_free); + -- cgit v1.2.1 From 7fee03e487e87a196deb5602ee3c7676511995c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 2 Dec 2009 15:56:47 -0300 Subject: V4L/DVB (13540): ir-common: Cleanup get key evdev code The same loop to seek for a key were used on different places. Also, no spinlock were protecting it to avoid the risk of replacing a keycode while seeking for a new code. This cleanup does: - create an unique function to seek for a code; - adds an spinlock to protect the table lookup; - remove some unused code; - simplifies to code to make it easier to understand. Basically no change in behavior should be noticed after this patch. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keytable.c | 137 ++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 62 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c index 6e3cc78c33b2..b06f023ca760 100644 --- a/drivers/media/common/ir-keytable.c +++ b/drivers/media/common/ir-keytable.c @@ -9,6 +9,38 @@ #define IR_TAB_MIN_SIZE 32 +/** + * ir_seek_table() - returns the element order on the table + * @rc_tab: the ir_scancode_table with the keymap to be used + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode) +{ + int rc; + unsigned long flags; + struct ir_scancode *keymap = rc_tab->scan; + + spin_lock_irqsave(&rc_tab->lock, flags); + + /* FIXME: replace it by a binary search */ + + for (rc = 0; rc < rc_tab->size; rc++) + if (keymap[rc].scancode == scancode) + goto exit; + + /* Not found */ + rc = -EINVAL; + +exit: + spin_unlock_irqrestore(&rc_tab->lock, flags); + return rc; +} + /** * ir_roundup_tablesize() - gets an optimum value for the table size * @n_elems: minimum number of entries to store keycodes @@ -54,21 +86,20 @@ int ir_copy_table(struct ir_scancode_table *destin, int i, j = 0; for (i = 0; i < origin->size; i++) { - if (origin->scan[i].keycode != KEY_UNKNOWN && - origin->scan[i].keycode != KEY_RESERVED) { - memcpy(&destin->scan[j], &origin->scan[i], - sizeof(struct ir_scancode)); - j++; - } + if (origin->scan[i].keycode == KEY_UNKNOWN || + origin->scan[i].keycode == KEY_RESERVED) + continue; + + memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode)); + j++; } destin->size = j; - IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", j); + IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size); return 0; } - /** * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table * @dev: the struct input_dev device descriptor @@ -81,28 +112,14 @@ int ir_copy_table(struct ir_scancode_table *destin, static int ir_getkeycode(struct input_dev *dev, int scancode, int *keycode) { - int i; + int elem; struct ir_scancode_table *rc_tab = input_get_drvdata(dev); - struct ir_scancode *keymap = rc_tab->scan; - - /* See if we can match the raw key code. */ - for (i = 0; i < rc_tab->size; i++) - if (keymap[i].scancode == scancode) { - *keycode = keymap[i].keycode; - return 0; - } - /* - * If is there extra space, returns KEY_RESERVED, - * otherwise, input core won't let ir_setkeycode - * to work - */ - for (i = 0; i < rc_tab->size; i++) - if (keymap[i].keycode == KEY_RESERVED || - keymap[i].keycode == KEY_UNKNOWN) { - *keycode = KEY_RESERVED; - return 0; - } + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + *keycode = rc_tab->scan[elem].keycode; + return 0; + } return -EINVAL; } @@ -120,40 +137,33 @@ static int ir_getkeycode(struct input_dev *dev, static int ir_setkeycode(struct input_dev *dev, int scancode, int keycode) { - int i; + int rc = 0; struct ir_scancode_table *rc_tab = input_get_drvdata(dev); struct ir_scancode *keymap = rc_tab->scan; + unsigned long flags; /* Search if it is replacing an existing keycode */ - for (i = 0; i < rc_tab->size; i++) - if (keymap[i].scancode == scancode) { - keymap[i].keycode = keycode; - return 0; - } - - /* Search if is there a clean entry. If so, use it */ - for (i = 0; i < rc_tab->size; i++) - if (keymap[i].keycode == KEY_RESERVED || - keymap[i].keycode == KEY_UNKNOWN) { - keymap[i].scancode = scancode; - keymap[i].keycode = keycode; - return 0; - } + rc = ir_seek_table(rc_tab, scancode); + if (rc <0) + return rc; - /* - * FIXME: Currently, it is not possible to increase the size of - * scancode table. For it to happen, one possibility - * would be to allocate a table with key_map_size + 1, - * copying data, appending the new key on it, and freeing - * the old one - or maybe just allocating some spare space - */ + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + rc, scancode, keycode); - return -EINVAL; + clear_bit(keymap[rc].keycode, dev->keybit); + + spin_lock_irqsave(&rc_tab->lock, flags); + keymap[rc].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + set_bit(keycode, dev->keybit); + + return 0; } /** * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode - * @rc_tab: the ir_scancode_table with the keymap to be used + * @input_dev: the struct input_dev descriptor of the device * @scancode: the scancode that we're seeking * * This routine is used by the input routines when a key is pressed at the @@ -163,22 +173,23 @@ static int ir_setkeycode(struct input_dev *dev, */ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) { - int i; struct ir_scancode_table *rc_tab = input_get_drvdata(dev); struct ir_scancode *keymap = rc_tab->scan; + int elem; - for (i = 0; i < rc_tab->size; i++) - if (keymap[i].scancode == scancode) { - IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", - dev->name, scancode, keymap[i].keycode); + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->name, scancode, keymap[elem].keycode); - return keymap[i].keycode; - } + return rc_tab->scan[elem].keycode; + } printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n", dev->name, scancode); - return KEY_UNKNOWN; + /* Reports userspace that an unknown keycode were got */ + return KEY_RESERVED; } /** @@ -188,8 +199,8 @@ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) * @rc_tab: the struct ir_scancode_table table of scancode/keymap * * This routine is used to initialize the input infrastructure to work with - * an IR. It requires that the caller initializes the input_dev struct with - * some fields: name, + * an IR. + * It should be called before registering the IR device. */ int ir_set_keycode_table(struct input_dev *input_dev, struct ir_scancode_table *rc_tab) @@ -197,6 +208,8 @@ int ir_set_keycode_table(struct input_dev *input_dev, struct ir_scancode *keymap = rc_tab->scan; int i; + spin_lock_init(&rc_tab->lock); + if (rc_tab->scan == NULL || !rc_tab->size) return -EINVAL; -- cgit v1.2.1 From e97f4677961f68e29bd906022ebf60a6df7f530a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Dec 2009 17:17:47 -0300 Subject: V4L/DVB (13542): ir-keytable: Allow dynamic table change Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keytable.c | 207 +++++++++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 10 deletions(-) (limited to 'drivers/media/common') diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c index b06f023ca760..26ce5bc2fdd5 100644 --- a/drivers/media/common/ir-keytable.c +++ b/drivers/media/common/ir-keytable.c @@ -8,6 +8,7 @@ #include #define IR_TAB_MIN_SIZE 32 +#define IR_TAB_MAX_SIZE 1024 /** * ir_seek_table() - returns the element order on the table @@ -121,7 +122,154 @@ static int ir_getkeycode(struct input_dev *dev, return 0; } - return -EINVAL; + /* + * Scancode not found and table can't be expanded + */ + if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE) + return -EINVAL; + + /* + * If is there extra space, returns KEY_RESERVED, + * otherwise, input core won't let ir_setkeycode to work + */ + *keycode = KEY_RESERVED; + return 0; +} + + +/** + * ir_is_resize_needed() - Check if the table needs rezise + * @table: keycode table that may need to resize + * @n_elems: minimum number of entries to store keycodes + * + * Considering that kmalloc uses power of two storage areas, this + * routine detects if the real alloced size will change. If not, it + * just returns without doing nothing. Otherwise, it will extend or + * reduce the table size to meet the new needs. + * + * It returns 0 if no resize is needed, 1 otherwise. + */ +static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems) +{ + int cur_size = ir_roundup_tablesize(table->size); + int new_size = ir_roundup_tablesize(n_elems); + + if (cur_size == new_size) + return 0; + + /* Resize is needed */ + return 1; +} + +/** + * ir_delete_key() - remove a keycode from the table + * @rc_tab: keycode table + * @elem: element to be removed + * + */ +static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) +{ + unsigned long flags = 0; + int newsize = rc_tab->size - 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + + /* There's no memory for resize. Keep the old table */ + if (!newkeymap) + resize = 0; + } + + if (!resize) { + newkeymap = oldkeymap; + + /* We'll modify the live table. Lock it */ + spin_lock_irqsave(&rc_tab->lock, flags); + } + + /* + * Copy the elements before the one that will be deleted + * if (!resize), both oldkeymap and newkeymap points + * to the same place, so, there's no need to copy + */ + if (resize && elem > 0) + memcpy(newkeymap, oldkeymap, + elem * sizeof(*newkeymap)); + + /* + * Copy the other elements overwriting the element to be removed + * This operation applies to both resize and non-resize case + */ + if (elem < newsize) + memcpy(&newkeymap[elem], &oldkeymap[elem + 1], + (newsize - elem) * sizeof(*newkeymap)); + + if (resize) { + /* + * As the copy happened to a temporary table, only here + * it needs to lock while replacing the table pointers + * to use the new table + */ + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + rc_tab->scan = newkeymap; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + /* Frees the old keytable */ + kfree(oldkeymap); + } else { + rc_tab->size = newsize; + spin_unlock_irqrestore(&rc_tab->lock, flags); + } +} + +/** + * ir_insert_key() - insert a keycode at the table + * @rc_tab: keycode table + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + */ +static int ir_insert_key(struct ir_scancode_table *rc_tab, + int scancode, int keycode) +{ + unsigned long flags; + int elem = rc_tab->size; + int newsize = rc_tab->size + 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + if (!newkeymap) + return -ENOMEM; + + memcpy(newkeymap, oldkeymap, + rc_tab->size * sizeof(*newkeymap)); + } else + newkeymap = oldkeymap; + + /* Stores the new code at the table */ + IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", + rc_tab->size, scancode, keycode); + + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + if (resize) { + rc_tab->scan = newkeymap; + kfree(oldkeymap); + } + newkeymap[elem].scancode = scancode; + newkeymap[elem].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + return 0; } /** @@ -142,20 +290,59 @@ static int ir_setkeycode(struct input_dev *dev, struct ir_scancode *keymap = rc_tab->scan; unsigned long flags; - /* Search if it is replacing an existing keycode */ + /* + * Handle keycode table deletions + * + * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED, + * deal as a trial to remove an existing scancode attribution + * if table become too big, reduce it to save space + */ + if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) { + rc = ir_seek_table(rc_tab, scancode); + if (rc < 0) + return 0; + + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode); + clear_bit(keymap[rc].keycode, dev->keybit); + ir_delete_key(rc_tab, rc); + + return 0; + } + + /* + * Handle keycode replacements + * + * If the scancode exists, just replace by the new value + */ rc = ir_seek_table(rc_tab, scancode); - if (rc <0) - return rc; + if (rc >= 0) { + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + rc, scancode, keycode); - IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", - rc, scancode, keycode); + clear_bit(keymap[rc].keycode, dev->keybit); - clear_bit(keymap[rc].keycode, dev->keybit); + spin_lock_irqsave(&rc_tab->lock, flags); + keymap[rc].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); - spin_lock_irqsave(&rc_tab->lock, flags); - keymap[rc].keycode = keycode; - spin_unlock_irqrestore(&rc_tab->lock, flags); + set_bit(keycode, dev->keybit); + return 0; + } + + /* + * Handle new scancode inserts + * + * reallocate table if needed and insert a new keycode + */ + + /* Avoid growing the table indefinitely */ + if (rc_tab->size + 1 > IR_TAB_MAX_SIZE) + return -EINVAL; + + rc = ir_insert_key(rc_tab, scancode, keycode); + if (rc < 0) + return rc; set_bit(keycode, dev->keybit); return 0; -- cgit v1.2.1