aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/common
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-09 19:50:49 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-09 19:50:49 -0800
commit3e7468313758913c5e4d372f35b271b96bad1298 (patch)
treeeb612d252a9e2349a1173451cd779beebd18a33e /drivers/media/common
parentMerge branch 'next-i2c' of git://git.fluff.org/bjdooks/linux (diff)
parentV4L/DVB (13542): ir-keytable: Allow dynamic table change (diff)
downloadlinux-dev-3e7468313758913c5e4d372f35b271b96bad1298.tar.xz
linux-dev-3e7468313758913c5e4d372f35b271b96bad1298.zip
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (345 commits) V4L/DVB (13542): ir-keytable: Allow dynamic table change V4L/DVB (13541): atbm8830: replace 64-bit division and floating point usage V4L/DVB (13540): ir-common: Cleanup get key evdev code V4L/DVB (13539): ir-common: add __func__ for debug messages V4L/DVB (13538): ir-common: Use a dynamic keycode table V4L/DVB (13537): ir: Prepare the code for dynamic keycode table allocation V4L/DVB (13536): em28xx: Use the full RC5 code on HVR-950 Remote Controller V4L/DVB (13535): ir-common: Add a hauppauge new table with the complete RC5 code V4L/DVB (13534): ir-common: Remove some unused fields/structs V4L/DVB (13533): ir: use dynamic tables, instead of static ones V4L/DVB (13532): ir-common: Add infrastructure to use a dynamic keycode table V4L/DVB (13531): ir-common: rename the debug routine to allow exporting it V4L/DVB (13458): go7007: subdev conversion V4L/DVB (13457): s2250: subdev conversion V4L/DVB (13456): s2250: Change module structure V4L/DVB (13528): em28xx: add support for em2800 VC211A card em28xx: don't reduce scale to half size for em2800 em28xx: don't load audio modules when AC97 is mis-detected em28xx: em2800 chips support max width of 640 V4L/DVB (13523): dvb-bt8xx: fix compile warning ... Fix up trivial conflicts due to spelling fixes from the trivial tree in Documentation/video4linux/gspca.txt drivers/media/video/cx18/cx18-mailbox.h
Diffstat (limited to 'drivers/media/common')
-rw-r--r--drivers/media/common/Makefile2
-rw-r--r--drivers/media/common/ir-functions.c71
-rw-r--r--drivers/media/common/ir-keymaps.c167
-rw-r--r--drivers/media/common/ir-keytable.c429
-rw-r--r--drivers/media/common/saa7146_video.c16
-rw-r--r--drivers/media/common/tuners/Kconfig7
-rw-r--r--drivers/media/common/tuners/Makefile1
-rw-r--r--drivers/media/common/tuners/max2165.c442
-rw-r--r--drivers/media/common/tuners/max2165.h48
-rw-r--r--drivers/media/common/tuners/max2165_priv.h60
-rw-r--r--drivers/media/common/tuners/mxl5005s.c5
-rw-r--r--drivers/media/common/tuners/mxl5005s.h4
-rw-r--r--drivers/media/common/tuners/mxl5007t.c2
-rw-r--r--drivers/media/common/tuners/tda18271-common.c16
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c114
-rw-r--r--drivers/media/common/tuners/tda18271-maps.c1
-rw-r--r--drivers/media/common/tuners/tda18271-priv.h47
-rw-r--r--drivers/media/common/tuners/tda18271.h12
-rw-r--r--drivers/media/common/tuners/tda8290.c1
-rw-r--r--drivers/media/common/tuners/tda9887.c2
-rw-r--r--drivers/media/common/tuners/xc5000.c97
-rw-r--r--drivers/media/common/tuners/xc5000.h6
22 files changed, 1431 insertions, 119 deletions
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-functions.c b/drivers/media/common/ir-functions.c
index abd4791acb0e..e616f624ceaa 100644
--- a/drivers/media/common/ir-functions.c
+++ b/drivers/media/common/ir-functions.c
@@ -34,22 +34,19 @@ 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);
/* -------------------------------------------------------------------------- */
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;
}
- 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);
@@ -57,39 +54,34 @@ 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)
{
- int i;
-
ir->ir_type = ir_type;
- memset(ir->ir_codes, 0, sizeof(ir->ir_codes));
+ 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;
- /*
- * 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
- */
+ IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
+ ir->keytable.size,
+ ir->keytable.size * sizeof(ir->keytable.scan));
- 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_copy_table(&ir->keytable, ir_codes);
+ ir_set_keycode_table(dev, &ir->keytable);
- 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);
if (repeat)
set_bit(EV_REP, dev->evbit);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(ir_input_init);
+
void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
{
if (ir->keypressed) {
@@ -100,9 +92,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;
@@ -110,7 +102,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);
@@ -275,7 +266,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;
@@ -295,15 +286,16 @@ static 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;
}
+EXPORT_SYMBOL_GPL(ir_rc5_decode);
void ir_rc5_timer_end(unsigned long data)
{
@@ -330,20 +322,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) {
@@ -353,11 +345,10 @@ 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,
- instr);
+ ir_input_keydown(ir->dev, &ir->ir, instr);
}
/* Set/reset key-up timer */
@@ -376,7 +367,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);
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index f6790172736a..328c973a0838 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 */
@@ -1846,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 },
@@ -2964,6 +3035,101 @@ 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*/
+ { 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 <dheitmueller@linuxtv.org>
*/
@@ -3147,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);
+
diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c
new file mode 100644
index 000000000000..26ce5bc2fdd5
--- /dev/null
+++ b/drivers/media/common/ir-keytable.c
@@ -0,0 +1,429 @@
+/* ir-register.c - handle IR scancode->keycode tables
+ *
+ * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+
+#include <linux/usb/input.h>
+
+#include <media/ir-common.h>
+
+#define IR_TAB_MIN_SIZE 32
+#define IR_TAB_MAX_SIZE 1024
+
+/**
+ * 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
+ *
+ * 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)
+ 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", destin->size);
+
+ return 0;
+}
+
+/**
+ * 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 elem;
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+
+ elem = ir_seek_table(rc_tab, scancode);
+ if (elem >= 0) {
+ *keycode = rc_tab->scan[elem].keycode;
+ return 0;
+ }
+
+ /*
+ * 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;
+}
+
+/**
+ * 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 rc = 0;
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_scancode *keymap = rc_tab->scan;
+ unsigned long flags;
+
+ /*
+ * 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) {
+ IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n",
+ rc, scancode, keycode);
+
+ 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;
+ }
+
+ /*
+ * 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;
+}
+
+/**
+ * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode
+ * @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
+ * 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)
+{
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_scancode *keymap = rc_tab->scan;
+ int elem;
+
+ 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 rc_tab->scan[elem].keycode;
+ }
+
+ printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n",
+ dev->name, scancode);
+
+ /* Reports userspace that an unknown keycode were got */
+ return KEY_RESERVED;
+}
+
+/**
+ * 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 should be called before registering the IR device.
+ */
+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;
+
+ spin_lock_init(&rc_tab->lock);
+
+ 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;
+}
+
+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);
+
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);
}
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 <davidtlwong@gmail.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#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,
+ &quotient, &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 <davidtlwong@gmail.com>");
+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 <davidtlwong@gmail.com>
+ *
+ * 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 <davidtlwong@gmail.com>
+ *
+ * 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
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;
};
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;
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 3a50ce96fcb9..b2e15456d5f3 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;
@@ -572,6 +573,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
@@ -610,20 +612,22 @@ 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:
- 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);
- map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2];
+ 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 = (s32)(prog_cal[RF2] - prog_tab[RF2]);
map[i].rf3 = rf_freq[RF3] / 1000;
break;
default:
@@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe)
return ret;
}
+static int tda18271_setup_configuration(struct dvb_frontend *fe,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ 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;
+
+ 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;
+
+ tda18271_setup_configuration(fe, 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;
+}
+
static struct dvb_tuner_ops tda18271_tuner_ops = {
.info = {
.name = "NXP TDA18271HD",
@@ -1193,6 +1239,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,
};
@@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
case 0:
goto fail;
case 1:
- {
/* 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 : 0;
- priv->output_opt = (cfg) ?
- cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
-
- /* 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;
- }
+ fe->tuner_priv = priv;
+
+ tda18271_setup_configuration(fe, cfg);
priv->cal_initialized = false;
mutex_init(&priv->lock);
- fe->tuner_priv = priv;
-
if (tda_fail(tda18271_get_id(fe)))
goto fail;
@@ -1249,12 +1277,12 @@ 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);
break;
- }
default:
/* existing tuner instance */
fe->tuner_priv = priv;
@@ -1271,7 +1299,11 @@ 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);
}
+ if (tda18271_need_cal_on_startup(cfg))
+ tda18271_init(fe);
break;
}
@@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach);
MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.3");
+MODULE_VERSION("0.4");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
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 2bee229acd91..9589ab0576d2 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 {
@@ -109,11 +109,12 @@ 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 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;
@@ -135,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; \
})
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;
};
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");
/* ---------------------------------------------------------------------- */
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;
}
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index f4ffcdc9b848..432003dded7c 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 */
@@ -632,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);
@@ -739,15 +744,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 +829,86 @@ 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;
+ 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[radio_input].VideoMode,
+ XC5000_Standard[radio_input].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) {
+ 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:
+ 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;
@@ -1000,6 +1082,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.