diff options
Diffstat (limited to 'drivers/media')
334 files changed, 48730 insertions, 12607 deletions
diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index 16792a68a44..655474b29e2 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -58,13 +58,24 @@ 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, IR_KEYTAB_TYPE *ir_codes) + int ir_type, struct ir_scancode_table *ir_codes) { int i; ir->ir_type = ir_type; + + memset(ir->ir_codes, sizeof(ir->ir_codes), 0); + + /* + * 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) - memcpy(ir->ir_codes, ir_codes, sizeof(ir->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; dev->keycode = ir->ir_codes; dev->keycodesize = sizeof(IR_KEYTAB_TYPE); diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 4216328552f..f6790172736 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1,8 +1,6 @@ /* - - - Keytables for supported remote controls. This file is part of - video4linux. + Keytables for supported remote controls, used on drivers/media + devices. 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 @@ -17,7 +15,13 @@ 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. +*/ +/* + * NOTICE FOR DEVELOPERS: + * The IR mappings should be as close as possible to what's + * specified at: + * http://linuxtv.org/wiki/index.php/Remote_Controllers */ #include <linux/module.h> @@ -25,589 +29,627 @@ #include <media/ir-common.h> /* empty keytable, can be used as placeholder for not-yet created keytables */ -IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = { - [ 0x2a ] = KEY_COFFEE, +static struct ir_scancode ir_codes_empty[] = { + { 0x2a, KEY_COFFEE }, }; -EXPORT_SYMBOL_GPL(ir_codes_empty); +struct ir_scancode_table ir_codes_empty_table = { + .scan = ir_codes_empty, + .size = ARRAY_SIZE(ir_codes_empty), +}; +EXPORT_SYMBOL_GPL(ir_codes_empty_table); /* Michal Majchrowicz <mmajchrowicz@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_proteus_2309[] = { /* numeric */ - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x5c ] = KEY_POWER, /* power */ - [ 0x20 ] = KEY_F, /* full screen */ - [ 0x0f ] = KEY_BACKSPACE, /* recall */ - [ 0x1b ] = KEY_ENTER, /* mute */ - [ 0x41 ] = KEY_RECORD, /* record */ - [ 0x43 ] = KEY_STOP, /* stop */ - [ 0x16 ] = KEY_S, - [ 0x1a ] = KEY_Q, /* off */ - [ 0x2e ] = KEY_RED, - [ 0x1f ] = KEY_DOWN, /* channel - */ - [ 0x1c ] = KEY_UP, /* channel + */ - [ 0x10 ] = KEY_LEFT, /* volume - */ - [ 0x1e ] = KEY_RIGHT, /* volume + */ - [ 0x14 ] = KEY_F1, -}; - -EXPORT_SYMBOL_GPL(ir_codes_proteus_2309); + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x5c, KEY_POWER }, /* power */ + { 0x20, KEY_ZOOM }, /* full screen */ + { 0x0f, KEY_BACKSPACE }, /* recall */ + { 0x1b, KEY_ENTER }, /* mute */ + { 0x41, KEY_RECORD }, /* record */ + { 0x43, KEY_STOP }, /* stop */ + { 0x16, KEY_S }, + { 0x1a, KEY_POWER2 }, /* off */ + { 0x2e, KEY_RED }, + { 0x1f, KEY_CHANNELDOWN }, /* channel - */ + { 0x1c, KEY_CHANNELUP }, /* channel + */ + { 0x10, KEY_VOLUMEDOWN }, /* volume - */ + { 0x1e, KEY_VOLUMEUP }, /* volume + */ + { 0x14, KEY_F1 }, +}; + +struct ir_scancode_table ir_codes_proteus_2309_table = { + .scan = ir_codes_proteus_2309, + .size = ARRAY_SIZE(ir_codes_proteus_2309), +}; +EXPORT_SYMBOL_GPL(ir_codes_proteus_2309_table); + /* Matt Jesson <dvb@jesson.eclipse.co.uk */ -IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = { - [ 0x28 ] = KEY_0, //'0' / 'enter' - [ 0x22 ] = KEY_1, //'1' - [ 0x12 ] = KEY_2, //'2' / 'up arrow' - [ 0x32 ] = KEY_3, //'3' - [ 0x24 ] = KEY_4, //'4' / 'left arrow' - [ 0x14 ] = KEY_5, //'5' - [ 0x34 ] = KEY_6, //'6' / 'right arrow' - [ 0x26 ] = KEY_7, //'7' - [ 0x16 ] = KEY_8, //'8' / 'down arrow' - [ 0x36 ] = KEY_9, //'9' - - [ 0x20 ] = KEY_LIST, // 'source' - [ 0x10 ] = KEY_TEXT, // 'teletext' - [ 0x00 ] = KEY_POWER, // 'power' - [ 0x04 ] = KEY_AUDIO, // 'audio' - [ 0x06 ] = KEY_ZOOM, // 'full screen' - [ 0x18 ] = KEY_VIDEO, // 'display' - [ 0x38 ] = KEY_SEARCH, // 'loop' - [ 0x08 ] = KEY_INFO, // 'preview' - [ 0x2a ] = KEY_REWIND, // 'backward <<' - [ 0x1a ] = KEY_FASTFORWARD, // 'forward >>' - [ 0x3a ] = KEY_RECORD, // 'capture' - [ 0x0a ] = KEY_MUTE, // 'mute' - [ 0x2c ] = KEY_RECORD, // 'record' - [ 0x1c ] = KEY_PAUSE, // 'pause' - [ 0x3c ] = KEY_STOP, // 'stop' - [ 0x0c ] = KEY_PLAY, // 'play' - [ 0x2e ] = KEY_RED, // 'red' - [ 0x01 ] = KEY_BLUE, // 'blue' / 'cancel' - [ 0x0e ] = KEY_YELLOW, // 'yellow' / 'ok' - [ 0x21 ] = KEY_GREEN, // 'green' - [ 0x11 ] = KEY_CHANNELDOWN, // 'channel -' - [ 0x31 ] = KEY_CHANNELUP, // 'channel +' - [ 0x1e ] = KEY_VOLUMEDOWN, // 'volume -' - [ 0x3e ] = KEY_VOLUMEUP, // 'volume +' -}; - -EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt); +static struct ir_scancode ir_codes_avermedia_dvbt[] = { + { 0x28, KEY_0 }, /* '0' / 'enter' */ + { 0x22, KEY_1 }, /* '1' */ + { 0x12, KEY_2 }, /* '2' / 'up arrow' */ + { 0x32, KEY_3 }, /* '3' */ + { 0x24, KEY_4 }, /* '4' / 'left arrow' */ + { 0x14, KEY_5 }, /* '5' */ + { 0x34, KEY_6 }, /* '6' / 'right arrow' */ + { 0x26, KEY_7 }, /* '7' */ + { 0x16, KEY_8 }, /* '8' / 'down arrow' */ + { 0x36, KEY_9 }, /* '9' */ + + { 0x20, KEY_LIST }, /* 'source' */ + { 0x10, KEY_TEXT }, /* 'teletext' */ + { 0x00, KEY_POWER }, /* 'power' */ + { 0x04, KEY_AUDIO }, /* 'audio' */ + { 0x06, KEY_ZOOM }, /* 'full screen' */ + { 0x18, KEY_VIDEO }, /* 'display' */ + { 0x38, KEY_SEARCH }, /* 'loop' */ + { 0x08, KEY_INFO }, /* 'preview' */ + { 0x2a, KEY_REWIND }, /* 'backward <<' */ + { 0x1a, KEY_FASTFORWARD }, /* 'forward >>' */ + { 0x3a, KEY_RECORD }, /* 'capture' */ + { 0x0a, KEY_MUTE }, /* 'mute' */ + { 0x2c, KEY_RECORD }, /* 'record' */ + { 0x1c, KEY_PAUSE }, /* 'pause' */ + { 0x3c, KEY_STOP }, /* 'stop' */ + { 0x0c, KEY_PLAY }, /* 'play' */ + { 0x2e, KEY_RED }, /* 'red' */ + { 0x01, KEY_BLUE }, /* 'blue' / 'cancel' */ + { 0x0e, KEY_YELLOW }, /* 'yellow' / 'ok' */ + { 0x21, KEY_GREEN }, /* 'green' */ + { 0x11, KEY_CHANNELDOWN }, /* 'channel -' */ + { 0x31, KEY_CHANNELUP }, /* 'channel +' */ + { 0x1e, KEY_VOLUMEDOWN }, /* 'volume -' */ + { 0x3e, KEY_VOLUMEUP }, /* 'volume +' */ +}; + +struct ir_scancode_table ir_codes_avermedia_dvbt_table = { + .scan = ir_codes_avermedia_dvbt, + .size = ARRAY_SIZE(ir_codes_avermedia_dvbt), +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt_table); /* Mauro Carvalho Chehab <mchehab@infradead.org> */ -IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = { - [0x00] = KEY_POWER2, - [0x2e] = KEY_DOT, /* '.' */ - [0x01] = KEY_MODE, /* TV/FM */ - - [0x05] = KEY_1, - [0x06] = KEY_2, - [0x07] = KEY_3, - [0x09] = KEY_4, - [0x0a] = KEY_5, - [0x0b] = KEY_6, - [0x0d] = KEY_7, - [0x0e] = KEY_8, - [0x0f] = KEY_9, - [0x11] = KEY_0, - - [0x13] = KEY_RIGHT, /* -> */ - [0x12] = KEY_LEFT, /* <- */ - - [0x17] = KEY_SLEEP, /* Capturar Imagem */ - [0x10] = KEY_SHUFFLE, /* Amostra */ +static struct ir_scancode ir_codes_avermedia_m135a[] = { + { 0x00, KEY_POWER2 }, + { 0x2e, KEY_DOT }, /* '.' */ + { 0x01, KEY_MODE }, /* TV/FM */ + + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + { 0x11, KEY_0 }, + + { 0x13, KEY_RIGHT }, /* -> */ + { 0x12, KEY_LEFT }, /* <- */ + + { 0x17, KEY_SLEEP }, /* Capturar Imagem */ + { 0x10, KEY_SHUFFLE }, /* Amostra */ /* FIXME: The keys bellow aren't ok */ - [0x43] = KEY_CHANNELUP, - [0x42] = KEY_CHANNELDOWN, - [0x1f] = KEY_VOLUMEUP, - [0x1e] = KEY_VOLUMEDOWN, - [0x0c] = KEY_ENTER, + { 0x43, KEY_CHANNELUP }, + { 0x42, KEY_CHANNELDOWN }, + { 0x1f, KEY_VOLUMEUP }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x0c, KEY_ENTER }, - [0x14] = KEY_MUTE, - [0x08] = KEY_AUDIO, + { 0x14, KEY_MUTE }, + { 0x08, KEY_AUDIO }, - [0x03] = KEY_TEXT, - [0x04] = KEY_EPG, - [0x2b] = KEY_TV2, /* TV2 */ + { 0x03, KEY_TEXT }, + { 0x04, KEY_EPG }, + { 0x2b, KEY_TV2 }, /* TV2 */ - [0x1d] = KEY_RED, - [0x1c] = KEY_YELLOW, - [0x41] = KEY_GREEN, - [0x40] = KEY_BLUE, + { 0x1d, KEY_RED }, + { 0x1c, KEY_YELLOW }, + { 0x41, KEY_GREEN }, + { 0x40, KEY_BLUE }, - [0x1a] = KEY_PLAYPAUSE, - [0x19] = KEY_RECORD, - [0x18] = KEY_PLAY, - [0x1b] = KEY_STOP, + { 0x1a, KEY_PLAYPAUSE }, + { 0x19, KEY_RECORD }, + { 0x18, KEY_PLAY }, + { 0x1b, KEY_STOP }, }; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a); + +struct ir_scancode_table ir_codes_avermedia_m135a_table = { + .scan = ir_codes_avermedia_m135a, + .size = ARRAY_SIZE(ir_codes_avermedia_m135a), +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a_table); /* Oldrich Jedlicka <oldium.pro@seznam.cz> */ -IR_KEYTAB_TYPE ir_codes_avermedia_cardbus[IR_KEYTAB_SIZE] = { - [0x00] = KEY_POWER, - [0x01] = KEY_TUNER, /* TV/FM */ - [0x03] = KEY_TEXT, /* Teletext */ - [0x04] = KEY_EPG, - [0x05] = KEY_1, - [0x06] = KEY_2, - [0x07] = KEY_3, - [0x08] = KEY_AUDIO, - [0x09] = KEY_4, - [0x0a] = KEY_5, - [0x0b] = KEY_6, - [0x0c] = KEY_ZOOM, /* Full screen */ - [0x0d] = KEY_7, - [0x0e] = KEY_8, - [0x0f] = KEY_9, - [0x10] = KEY_PAGEUP, /* 16-CH PREV */ - [0x11] = KEY_0, - [0x12] = KEY_INFO, - [0x13] = KEY_AGAIN, /* CH RTN - channel return */ - [0x14] = KEY_MUTE, - [0x15] = KEY_EDIT, /* Autoscan */ - [0x17] = KEY_SAVE, /* Screenshot */ - [0x18] = KEY_PLAYPAUSE, - [0x19] = KEY_RECORD, - [0x1a] = KEY_PLAY, - [0x1b] = KEY_STOP, - [0x1c] = KEY_FASTFORWARD, - [0x1d] = KEY_REWIND, - [0x1e] = KEY_VOLUMEDOWN, - [0x1f] = KEY_VOLUMEUP, - [0x22] = KEY_SLEEP, /* Sleep */ - [0x23] = KEY_ZOOM, /* Aspect */ - [0x26] = KEY_SCREEN, /* Pos */ - [0x27] = KEY_ANGLE, /* Size */ - [0x28] = KEY_SELECT, /* Select */ - [0x29] = KEY_BLUE, /* Blue/Picture */ - [0x2a] = KEY_BACKSPACE, /* Back */ - [0x2b] = KEY_MEDIA, /* PIP (Picture-in-picture) */ - [0x2c] = KEY_DOWN, - [0x2e] = KEY_DOT, - [0x2f] = KEY_TV, /* Live TV */ - [0x32] = KEY_LEFT, - [0x33] = KEY_CLEAR, /* Clear */ - [0x35] = KEY_RED, /* Red/TV */ - [0x36] = KEY_UP, - [0x37] = KEY_HOME, /* Home */ - [0x39] = KEY_GREEN, /* Green/Video */ - [0x3d] = KEY_YELLOW, /* Yellow/Music */ - [0x3e] = KEY_OK, /* Ok */ - [0x3f] = KEY_RIGHT, - [0x40] = KEY_NEXT, /* Next */ - [0x41] = KEY_PREVIOUS, /* Previous */ - [0x42] = KEY_CHANNELDOWN, /* Channel down */ - [0x43] = KEY_CHANNELUP /* Channel up */ -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus); +static struct ir_scancode ir_codes_avermedia_cardbus[] = { + { 0x00, KEY_POWER }, + { 0x01, KEY_TUNER }, /* TV/FM */ + { 0x03, KEY_TEXT }, /* Teletext */ + { 0x04, KEY_EPG }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x08, KEY_AUDIO }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0c, KEY_ZOOM }, /* Full screen */ + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + { 0x10, KEY_PAGEUP }, /* 16-CH PREV */ + { 0x11, KEY_0 }, + { 0x12, KEY_INFO }, + { 0x13, KEY_AGAIN }, /* CH RTN - channel return */ + { 0x14, KEY_MUTE }, + { 0x15, KEY_EDIT }, /* Autoscan */ + { 0x17, KEY_SAVE }, /* Screenshot */ + { 0x18, KEY_PLAYPAUSE }, + { 0x19, KEY_RECORD }, + { 0x1a, KEY_PLAY }, + { 0x1b, KEY_STOP }, + { 0x1c, KEY_FASTFORWARD }, + { 0x1d, KEY_REWIND }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_VOLUMEUP }, + { 0x22, KEY_SLEEP }, /* Sleep */ + { 0x23, KEY_ZOOM }, /* Aspect */ + { 0x26, KEY_SCREEN }, /* Pos */ + { 0x27, KEY_ANGLE }, /* Size */ + { 0x28, KEY_SELECT }, /* Select */ + { 0x29, KEY_BLUE }, /* Blue/Picture */ + { 0x2a, KEY_BACKSPACE }, /* Back */ + { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */ + { 0x2c, KEY_DOWN }, + { 0x2e, KEY_DOT }, + { 0x2f, KEY_TV }, /* Live TV */ + { 0x32, KEY_LEFT }, + { 0x33, KEY_CLEAR }, /* Clear */ + { 0x35, KEY_RED }, /* Red/TV */ + { 0x36, KEY_UP }, + { 0x37, KEY_HOME }, /* Home */ + { 0x39, KEY_GREEN }, /* Green/Video */ + { 0x3d, KEY_YELLOW }, /* Yellow/Music */ + { 0x3e, KEY_OK }, /* Ok */ + { 0x3f, KEY_RIGHT }, + { 0x40, KEY_NEXT }, /* Next */ + { 0x41, KEY_PREVIOUS }, /* Previous */ + { 0x42, KEY_CHANNELDOWN }, /* Channel down */ + { 0x43, KEY_CHANNELUP }, /* Channel up */ +}; + +struct ir_scancode_table ir_codes_avermedia_cardbus_table = { + .scan = ir_codes_avermedia_cardbus, + .size = ARRAY_SIZE(ir_codes_avermedia_cardbus), +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus_table); /* Attila Kondoros <attila.kondoros@chello.hu> */ -IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = { - - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - [ 0x00 ] = KEY_0, - [ 0x17 ] = KEY_LAST, // +100 - [ 0x0a ] = KEY_LIST, // recall - - - [ 0x1c ] = KEY_TUNER, // TV/FM - [ 0x15 ] = KEY_SEARCH, // scan - [ 0x12 ] = KEY_POWER, // power - [ 0x1f ] = KEY_VOLUMEDOWN, // vol up - [ 0x1b ] = KEY_VOLUMEUP, // vol down - [ 0x1e ] = KEY_CHANNELDOWN, // chn up - [ 0x1a ] = KEY_CHANNELUP, // chn down - - [ 0x11 ] = KEY_VIDEO, // video - [ 0x0f ] = KEY_ZOOM, // full screen - [ 0x13 ] = KEY_MUTE, // mute/unmute - [ 0x10 ] = KEY_TEXT, // min - - [ 0x0d ] = KEY_STOP, // freeze - [ 0x0e ] = KEY_RECORD, // record - [ 0x1d ] = KEY_PLAYPAUSE, // stop - [ 0x19 ] = KEY_PLAY, // play - - [ 0x16 ] = KEY_GOTO, // osd - [ 0x14 ] = KEY_REFRESH, // default - [ 0x0c ] = KEY_KPPLUS, // fine tune >>>> - [ 0x18 ] = KEY_KPMINUS // fine tune <<<< -}; - -EXPORT_SYMBOL_GPL(ir_codes_apac_viewcomp); +static struct ir_scancode ir_codes_apac_viewcomp[] = { + + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x00, KEY_0 }, + { 0x17, KEY_LAST }, /* +100 */ + { 0x0a, KEY_LIST }, /* recall */ + + + { 0x1c, KEY_TUNER }, /* TV/FM */ + { 0x15, KEY_SEARCH }, /* scan */ + { 0x12, KEY_POWER }, /* power */ + { 0x1f, KEY_VOLUMEDOWN }, /* vol up */ + { 0x1b, KEY_VOLUMEUP }, /* vol down */ + { 0x1e, KEY_CHANNELDOWN }, /* chn up */ + { 0x1a, KEY_CHANNELUP }, /* chn down */ + + { 0x11, KEY_VIDEO }, /* video */ + { 0x0f, KEY_ZOOM }, /* full screen */ + { 0x13, KEY_MUTE }, /* mute/unmute */ + { 0x10, KEY_TEXT }, /* min */ + + { 0x0d, KEY_STOP }, /* freeze */ + { 0x0e, KEY_RECORD }, /* record */ + { 0x1d, KEY_PLAYPAUSE }, /* stop */ + { 0x19, KEY_PLAY }, /* play */ + + { 0x16, KEY_GOTO }, /* osd */ + { 0x14, KEY_REFRESH }, /* default */ + { 0x0c, KEY_KPPLUS }, /* fine tune >>>> */ + { 0x18, KEY_KPMINUS }, /* fine tune <<<< */ +}; + +struct ir_scancode_table ir_codes_apac_viewcomp_table = { + .scan = ir_codes_apac_viewcomp, + .size = ARRAY_SIZE(ir_codes_apac_viewcomp), +}; +EXPORT_SYMBOL_GPL(ir_codes_apac_viewcomp_table); /* ---------------------------------------------------------------------- */ -IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_pixelview[] = { - [ 0x1e ] = KEY_POWER, // power - [ 0x07 ] = KEY_MEDIA, // source - [ 0x1c ] = KEY_SEARCH, // scan + { 0x1e, KEY_POWER }, /* power */ + { 0x07, KEY_MEDIA }, /* source */ + { 0x1c, KEY_SEARCH }, /* scan */ -/* FIXME: duplicate keycodes? - * - * These four keys seem to share the same GPIO as CH+, CH-, <<< and >>> - * The GPIO values are - * 6397fb for both "Scan <" and "CH -", - * 639ffb for "Scan >" and "CH+", - * 6384fb for "Tune <" and "<<<", - * 638cfb for "Tune >" and ">>>", regardless of the mask. - * - * [ 0x17 ] = KEY_BACK, // fm scan << - * [ 0x1f ] = KEY_FORWARD, // fm scan >> - * - * [ 0x04 ] = KEY_LEFT, // fm tuning < - * [ 0x0c ] = KEY_RIGHT, // fm tuning > - * - * For now, these four keys are disabled. Pressing them will generate - * the CH+/CH-/<<</>>> events - */ - [ 0x03 ] = KEY_TUNER, // TV/FM + { 0x03, KEY_TUNER }, /* TV/FM */ - [ 0x00 ] = KEY_RECORD, - [ 0x08 ] = KEY_STOP, - [ 0x11 ] = KEY_PLAY, + { 0x00, KEY_RECORD }, + { 0x08, KEY_STOP }, + { 0x11, KEY_PLAY }, - [ 0x1a ] = KEY_PLAYPAUSE, // freeze - [ 0x19 ] = KEY_ZOOM, // zoom - [ 0x0f ] = KEY_TEXT, // min + { 0x1a, KEY_PLAYPAUSE }, /* freeze */ + { 0x19, KEY_ZOOM }, /* zoom */ + { 0x0f, KEY_TEXT }, /* min */ - [ 0x01 ] = KEY_1, - [ 0x0b ] = KEY_2, - [ 0x1b ] = KEY_3, - [ 0x05 ] = KEY_4, - [ 0x09 ] = KEY_5, - [ 0x15 ] = KEY_6, - [ 0x06 ] = KEY_7, - [ 0x0a ] = KEY_8, - [ 0x12 ] = KEY_9, - [ 0x02 ] = KEY_0, - [ 0x10 ] = KEY_LAST, // +100 - [ 0x13 ] = KEY_LIST, // recall + { 0x01, KEY_1 }, + { 0x0b, KEY_2 }, + { 0x1b, KEY_3 }, + { 0x05, KEY_4 }, + { 0x09, KEY_5 }, + { 0x15, KEY_6 }, + { 0x06, KEY_7 }, + { 0x0a, KEY_8 }, + { 0x12, KEY_9 }, + { 0x02, KEY_0 }, + { 0x10, KEY_LAST }, /* +100 */ + { 0x13, KEY_LIST }, /* recall */ - [ 0x1f ] = KEY_CHANNELUP, // chn down - [ 0x17 ] = KEY_CHANNELDOWN, // chn up - [ 0x16 ] = KEY_VOLUMEUP, // vol down - [ 0x14 ] = KEY_VOLUMEDOWN, // vol up + { 0x1f, KEY_CHANNELUP }, /* chn down */ + { 0x17, KEY_CHANNELDOWN }, /* chn up */ + { 0x16, KEY_VOLUMEUP }, /* vol down */ + { 0x14, KEY_VOLUMEDOWN }, /* vol up */ - [ 0x04 ] = KEY_KPMINUS, // <<< - [ 0x0e ] = KEY_SETUP, // function - [ 0x0c ] = KEY_KPPLUS, // >>> + { 0x04, KEY_KPMINUS }, /* <<< */ + { 0x0e, KEY_SETUP }, /* function */ + { 0x0c, KEY_KPPLUS }, /* >>> */ - [ 0x0d ] = KEY_GOTO, // mts - [ 0x1d ] = KEY_REFRESH, // reset - [ 0x18 ] = KEY_MUTE // mute/unmute + { 0x0d, KEY_GOTO }, /* mts */ + { 0x1d, KEY_REFRESH }, /* reset */ + { 0x18, KEY_MUTE }, /* mute/unmute */ }; -EXPORT_SYMBOL_GPL(ir_codes_pixelview); +struct ir_scancode_table ir_codes_pixelview_table = { + .scan = ir_codes_pixelview, + .size = ARRAY_SIZE(ir_codes_pixelview), +}; +EXPORT_SYMBOL_GPL(ir_codes_pixelview_table); /* Mauro Carvalho Chehab <mchehab@infradead.org> present on PV MPEG 8000GT */ -IR_KEYTAB_TYPE ir_codes_pixelview_new[IR_KEYTAB_SIZE] = { - [0x3c] = KEY_PAUSE, /* Timeshift */ - [0x12] = KEY_POWER, - - [0x3d] = KEY_1, - [0x38] = KEY_2, - [0x18] = KEY_3, - [0x35] = KEY_4, - [0x39] = KEY_5, - [0x15] = KEY_6, - [0x36] = KEY_7, - [0x3a] = KEY_8, - [0x1e] = KEY_9, - [0x3e] = KEY_0, - - [0x1c] = KEY_AGAIN, /* LOOP */ - [0x3f] = KEY_MEDIA, /* Source */ - [0x1f] = KEY_LAST, /* +100 */ - [0x1b] = KEY_MUTE, - - [0x17] = KEY_CHANNELDOWN, - [0x16] = KEY_CHANNELUP, - [0x10] = KEY_VOLUMEUP, - [0x14] = KEY_VOLUMEDOWN, - [0x13] = KEY_ZOOM, - - [0x19] = KEY_SHUFFLE, /* SNAPSHOT */ - [0x1a] = KEY_SEARCH, /* scan */ - - [0x37] = KEY_REWIND, /* << */ - [0x32] = KEY_RECORD, /* o (red) */ - [0x33] = KEY_FORWARD, /* >> */ - [0x11] = KEY_STOP, /* square */ - [0x3b] = KEY_PLAY, /* > */ - [0x30] = KEY_PLAYPAUSE, /* || */ - - [0x31] = KEY_TV, - [0x34] = KEY_RADIO, -}; -EXPORT_SYMBOL_GPL(ir_codes_pixelview_new); - -IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - [ 0x0a ] = KEY_TV, - [ 0x0b ] = KEY_AUX, - [ 0x0c ] = KEY_DVD, - [ 0x0d ] = KEY_POWER, - [ 0x0e ] = KEY_MHP, /* labelled 'Picture' */ - [ 0x0f ] = KEY_AUDIO, - [ 0x10 ] = KEY_INFO, - [ 0x11 ] = KEY_F13, /* 16:9 */ - [ 0x12 ] = KEY_F14, /* 14:9 */ - [ 0x13 ] = KEY_EPG, - [ 0x14 ] = KEY_EXIT, - [ 0x15 ] = KEY_MENU, - [ 0x16 ] = KEY_UP, - [ 0x17 ] = KEY_DOWN, - [ 0x18 ] = KEY_LEFT, - [ 0x19 ] = KEY_RIGHT, - [ 0x1a ] = KEY_ENTER, - [ 0x1b ] = KEY_CHANNELUP, - [ 0x1c ] = KEY_CHANNELDOWN, - [ 0x1d ] = KEY_VOLUMEUP, - [ 0x1e ] = KEY_VOLUMEDOWN, - [ 0x1f ] = KEY_RED, - [ 0x20 ] = KEY_GREEN, - [ 0x21 ] = KEY_YELLOW, - [ 0x22 ] = KEY_BLUE, - [ 0x23 ] = KEY_SUBTITLE, - [ 0x24 ] = KEY_F15, /* AD */ - [ 0x25 ] = KEY_TEXT, - [ 0x26 ] = KEY_MUTE, - [ 0x27 ] = KEY_REWIND, - [ 0x28 ] = KEY_STOP, - [ 0x29 ] = KEY_PLAY, - [ 0x2a ] = KEY_FASTFORWARD, - [ 0x2b ] = KEY_F16, /* chapter */ - [ 0x2c ] = KEY_PAUSE, - [ 0x2d ] = KEY_PLAY, - [ 0x2e ] = KEY_RECORD, - [ 0x2f ] = KEY_F17, /* picture in picture */ - [ 0x30 ] = KEY_KPPLUS, /* zoom in */ - [ 0x31 ] = KEY_KPMINUS, /* zoom out */ - [ 0x32 ] = KEY_F18, /* capture */ - [ 0x33 ] = KEY_F19, /* web */ - [ 0x34 ] = KEY_EMAIL, - [ 0x35 ] = KEY_PHONE, - [ 0x36 ] = KEY_PC -}; - -EXPORT_SYMBOL_GPL(ir_codes_nebula); +static struct ir_scancode ir_codes_pixelview_new[] = { + { 0x3c, KEY_TIME }, /* Timeshift */ + { 0x12, KEY_POWER }, + + { 0x3d, KEY_1 }, + { 0x38, KEY_2 }, + { 0x18, KEY_3 }, + { 0x35, KEY_4 }, + { 0x39, KEY_5 }, + { 0x15, KEY_6 }, + { 0x36, KEY_7 }, + { 0x3a, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x3e, KEY_0 }, + + { 0x1c, KEY_AGAIN }, /* LOOP */ + { 0x3f, KEY_MEDIA }, /* Source */ + { 0x1f, KEY_LAST }, /* +100 */ + { 0x1b, KEY_MUTE }, + + { 0x17, KEY_CHANNELDOWN }, + { 0x16, KEY_CHANNELUP }, + { 0x10, KEY_VOLUMEUP }, + { 0x14, KEY_VOLUMEDOWN }, + { 0x13, KEY_ZOOM }, + + { 0x19, KEY_CAMERA }, /* SNAPSHOT */ + { 0x1a, KEY_SEARCH }, /* scan */ + + { 0x37, KEY_REWIND }, /* << */ + { 0x32, KEY_RECORD }, /* o (red) */ + { 0x33, KEY_FORWARD }, /* >> */ + { 0x11, KEY_STOP }, /* square */ + { 0x3b, KEY_PLAY }, /* > */ + { 0x30, KEY_PLAYPAUSE }, /* || */ + + { 0x31, KEY_TV }, + { 0x34, KEY_RADIO }, +}; + +struct ir_scancode_table ir_codes_pixelview_new_table = { + .scan = ir_codes_pixelview_new, + .size = ARRAY_SIZE(ir_codes_pixelview_new), +}; +EXPORT_SYMBOL_GPL(ir_codes_pixelview_new_table); + +static struct ir_scancode ir_codes_nebula[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_TV }, + { 0x0b, KEY_AUX }, + { 0x0c, KEY_DVD }, + { 0x0d, KEY_POWER }, + { 0x0e, KEY_MHP }, /* labelled 'Picture' */ + { 0x0f, KEY_AUDIO }, + { 0x10, KEY_INFO }, + { 0x11, KEY_F13 }, /* 16:9 */ + { 0x12, KEY_F14 }, /* 14:9 */ + { 0x13, KEY_EPG }, + { 0x14, KEY_EXIT }, + { 0x15, KEY_MENU }, + { 0x16, KEY_UP }, + { 0x17, KEY_DOWN }, + { 0x18, KEY_LEFT }, + { 0x19, KEY_RIGHT }, + { 0x1a, KEY_ENTER }, + { 0x1b, KEY_CHANNELUP }, + { 0x1c, KEY_CHANNELDOWN }, + { 0x1d, KEY_VOLUMEUP }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_RED }, + { 0x20, KEY_GREEN }, + { 0x21, KEY_YELLOW }, + { 0x22, KEY_BLUE }, + { 0x23, KEY_SUBTITLE }, + { 0x24, KEY_F15 }, /* AD */ + { 0x25, KEY_TEXT }, + { 0x26, KEY_MUTE }, + { 0x27, KEY_REWIND }, + { 0x28, KEY_STOP }, + { 0x29, KEY_PLAY }, + { 0x2a, KEY_FASTFORWARD }, + { 0x2b, KEY_F16 }, /* chapter */ + { 0x2c, KEY_PAUSE }, + { 0x2d, KEY_PLAY }, + { 0x2e, KEY_RECORD }, + { 0x2f, KEY_F17 }, /* picture in picture */ + { 0x30, KEY_KPPLUS }, /* zoom in */ + { 0x31, KEY_KPMINUS }, /* zoom out */ + { 0x32, KEY_F18 }, /* capture */ + { 0x33, KEY_F19 }, /* web */ + { 0x34, KEY_EMAIL }, + { 0x35, KEY_PHONE }, + { 0x36, KEY_PC }, +}; + +struct ir_scancode_table ir_codes_nebula_table = { + .scan = ir_codes_nebula, + .size = ARRAY_SIZE(ir_codes_nebula), +}; +EXPORT_SYMBOL_GPL(ir_codes_nebula_table); /* DigitalNow DNTV Live DVB-T Remote */ -IR_KEYTAB_TYPE ir_codes_dntv_live_dvb_t[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_ESC, /* 'go up a level?' */ +static struct ir_scancode ir_codes_dntv_live_dvb_t[] = { + { 0x00, KEY_ESC }, /* 'go up a level?' */ /* Keys 0 to 9 */ - [ 0x0a ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0b ] = KEY_TUNER, /* tv/fm */ - [ 0x0c ] = KEY_SEARCH, /* scan */ - [ 0x0d ] = KEY_STOP, - [ 0x0e ] = KEY_PAUSE, - [ 0x0f ] = KEY_LIST, /* source */ - - [ 0x10 ] = KEY_MUTE, - [ 0x11 ] = KEY_REWIND, /* backward << */ - [ 0x12 ] = KEY_POWER, - [ 0x13 ] = KEY_S, /* snap */ - [ 0x14 ] = KEY_AUDIO, /* stereo */ - [ 0x15 ] = KEY_CLEAR, /* reset */ - [ 0x16 ] = KEY_PLAY, - [ 0x17 ] = KEY_ENTER, - [ 0x18 ] = KEY_ZOOM, /* full screen */ - [ 0x19 ] = KEY_FASTFORWARD, /* forward >> */ - [ 0x1a ] = KEY_CHANNELUP, - [ 0x1b ] = KEY_VOLUMEUP, - [ 0x1c ] = KEY_INFO, /* preview */ - [ 0x1d ] = KEY_RECORD, /* record */ - [ 0x1e ] = KEY_CHANNELDOWN, - [ 0x1f ] = KEY_VOLUMEDOWN, -}; - -EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvb_t); + { 0x0a, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0b, KEY_TUNER }, /* tv/fm */ + { 0x0c, KEY_SEARCH }, /* scan */ + { 0x0d, KEY_STOP }, + { 0x0e, KEY_PAUSE }, + { 0x0f, KEY_LIST }, /* source */ + + { 0x10, KEY_MUTE }, + { 0x11, KEY_REWIND }, /* backward << */ + { 0x12, KEY_POWER }, + { 0x13, KEY_CAMERA }, /* snap */ + { 0x14, KEY_AUDIO }, /* stereo */ + { 0x15, KEY_CLEAR }, /* reset */ + { 0x16, KEY_PLAY }, + { 0x17, KEY_ENTER }, + { 0x18, KEY_ZOOM }, /* full screen */ + { 0x19, KEY_FASTFORWARD }, /* forward >> */ + { 0x1a, KEY_CHANNELUP }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1c, KEY_INFO }, /* preview */ + { 0x1d, KEY_RECORD }, /* record */ + { 0x1e, KEY_CHANNELDOWN }, + { 0x1f, KEY_VOLUMEDOWN }, +}; + +struct ir_scancode_table ir_codes_dntv_live_dvb_t_table = { + .scan = ir_codes_dntv_live_dvb_t, + .size = ARRAY_SIZE(ir_codes_dntv_live_dvb_t), +}; +EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvb_t_table); /* ---------------------------------------------------------------------- */ /* IO-DATA BCTV7E Remote */ -IR_KEYTAB_TYPE ir_codes_iodata_bctv7e[IR_KEYTAB_SIZE] = { - [ 0x40 ] = KEY_TV, - [ 0x20 ] = KEY_RADIO, /* FM */ - [ 0x60 ] = KEY_EPG, - [ 0x00 ] = KEY_POWER, +static struct ir_scancode ir_codes_iodata_bctv7e[] = { + { 0x40, KEY_TV }, + { 0x20, KEY_RADIO }, /* FM */ + { 0x60, KEY_EPG }, + { 0x00, KEY_POWER }, /* Keys 0 to 9 */ - [ 0x44 ] = KEY_0, /* 10 */ - [ 0x50 ] = KEY_1, - [ 0x30 ] = KEY_2, - [ 0x70 ] = KEY_3, - [ 0x48 ] = KEY_4, - [ 0x28 ] = KEY_5, - [ 0x68 ] = KEY_6, - [ 0x58 ] = KEY_7, - [ 0x38 ] = KEY_8, - [ 0x78 ] = KEY_9, - - [ 0x10 ] = KEY_L, /* Live */ - [ 0x08 ] = KEY_T, /* Time Shift */ - - [ 0x18 ] = KEY_PLAYPAUSE, /* Play */ - - [ 0x24 ] = KEY_ENTER, /* 11 */ - [ 0x64 ] = KEY_ESC, /* 12 */ - [ 0x04 ] = KEY_M, /* Multi */ - - [ 0x54 ] = KEY_VIDEO, - [ 0x34 ] = KEY_CHANNELUP, - [ 0x74 ] = KEY_VOLUMEUP, - [ 0x14 ] = KEY_MUTE, - - [ 0x4c ] = KEY_S, /* SVIDEO */ - [ 0x2c ] = KEY_CHANNELDOWN, - [ 0x6c ] = KEY_VOLUMEDOWN, - [ 0x0c ] = KEY_ZOOM, - - [ 0x5c ] = KEY_PAUSE, - [ 0x3c ] = KEY_C, /* || (red) */ - [ 0x7c ] = KEY_RECORD, /* recording */ - [ 0x1c ] = KEY_STOP, - - [ 0x41 ] = KEY_REWIND, /* backward << */ - [ 0x21 ] = KEY_PLAY, - [ 0x61 ] = KEY_FASTFORWARD, /* forward >> */ - [ 0x01 ] = KEY_NEXT, /* skip >| */ + { 0x44, KEY_0 }, /* 10 */ + { 0x50, KEY_1 }, + { 0x30, KEY_2 }, + { 0x70, KEY_3 }, + { 0x48, KEY_4 }, + { 0x28, KEY_5 }, + { 0x68, KEY_6 }, + { 0x58, KEY_7 }, + { 0x38, KEY_8 }, + { 0x78, KEY_9 }, + + { 0x10, KEY_L }, /* Live */ + { 0x08, KEY_TIME }, /* Time Shift */ + + { 0x18, KEY_PLAYPAUSE }, /* Play */ + + { 0x24, KEY_ENTER }, /* 11 */ + { 0x64, KEY_ESC }, /* 12 */ + { 0x04, KEY_M }, /* Multi */ + + { 0x54, KEY_VIDEO }, + { 0x34, KEY_CHANNELUP }, + { 0x74, KEY_VOLUMEUP }, + { 0x14, KEY_MUTE }, + + { 0x4c, KEY_VCR }, /* SVIDEO */ + { 0x2c, KEY_CHANNELDOWN }, + { 0x6c, KEY_VOLUMEDOWN }, + { 0x0c, KEY_ZOOM }, + + { 0x5c, KEY_PAUSE }, + { 0x3c, KEY_RED }, /* || (red) */ + { 0x7c, KEY_RECORD }, /* recording */ + { 0x1c, KEY_STOP }, + + { 0x41, KEY_REWIND }, /* backward << */ + { 0x21, KEY_PLAY }, + { 0x61, KEY_FASTFORWARD }, /* forward >> */ + { 0x01, KEY_NEXT }, /* skip >| */ }; -EXPORT_SYMBOL_GPL(ir_codes_iodata_bctv7e); +struct ir_scancode_table ir_codes_iodata_bctv7e_table = { + .scan = ir_codes_iodata_bctv7e, + .size = ARRAY_SIZE(ir_codes_iodata_bctv7e), +}; +EXPORT_SYMBOL_GPL(ir_codes_iodata_bctv7e_table); /* ---------------------------------------------------------------------- */ /* ADS Tech Instant TV DVB-T PCI Remote */ -IR_KEYTAB_TYPE ir_codes_adstech_dvb_t_pci[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_adstech_dvb_t_pci[] = { /* Keys 0 to 9 */ - [ 0x4d ] = KEY_0, - [ 0x57 ] = KEY_1, - [ 0x4f ] = KEY_2, - [ 0x53 ] = KEY_3, - [ 0x56 ] = KEY_4, - [ 0x4e ] = KEY_5, - [ 0x5e ] = KEY_6, - [ 0x54 ] = KEY_7, - [ 0x4c ] = KEY_8, - [ 0x5c ] = KEY_9, - - [ 0x5b ] = KEY_POWER, - [ 0x5f ] = KEY_MUTE, - [ 0x55 ] = KEY_GOTO, - [ 0x5d ] = KEY_SEARCH, - [ 0x17 ] = KEY_EPG, /* Guide */ - [ 0x1f ] = KEY_MENU, - [ 0x0f ] = KEY_UP, - [ 0x46 ] = KEY_DOWN, - [ 0x16 ] = KEY_LEFT, - [ 0x1e ] = KEY_RIGHT, - [ 0x0e ] = KEY_SELECT, /* Enter */ - [ 0x5a ] = KEY_INFO, - [ 0x52 ] = KEY_EXIT, - [ 0x59 ] = KEY_PREVIOUS, - [ 0x51 ] = KEY_NEXT, - [ 0x58 ] = KEY_REWIND, - [ 0x50 ] = KEY_FORWARD, - [ 0x44 ] = KEY_PLAYPAUSE, - [ 0x07 ] = KEY_STOP, - [ 0x1b ] = KEY_RECORD, - [ 0x13 ] = KEY_TUNER, /* Live */ - [ 0x0a ] = KEY_A, - [ 0x12 ] = KEY_B, - [ 0x03 ] = KEY_PROG1, /* 1 */ - [ 0x01 ] = KEY_PROG2, /* 2 */ - [ 0x00 ] = KEY_PROG3, /* 3 */ - [ 0x06 ] = KEY_DVD, - [ 0x48 ] = KEY_AUX, /* Photo */ - [ 0x40 ] = KEY_VIDEO, - [ 0x19 ] = KEY_AUDIO, /* Music */ - [ 0x0b ] = KEY_CHANNELUP, - [ 0x08 ] = KEY_CHANNELDOWN, - [ 0x15 ] = KEY_VOLUMEUP, - [ 0x1c ] = KEY_VOLUMEDOWN, -}; - -EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci); + { 0x4d, KEY_0 }, + { 0x57, KEY_1 }, + { 0x4f, KEY_2 }, + { 0x53, KEY_3 }, + { 0x56, KEY_4 }, + { 0x4e, KEY_5 }, + { 0x5e, KEY_6 }, + { 0x54, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x5c, KEY_9 }, + + { 0x5b, KEY_POWER }, + { 0x5f, KEY_MUTE }, + { 0x55, KEY_GOTO }, + { 0x5d, KEY_SEARCH }, + { 0x17, KEY_EPG }, /* Guide */ + { 0x1f, KEY_MENU }, + { 0x0f, KEY_UP }, + { 0x46, KEY_DOWN }, + { 0x16, KEY_LEFT }, + { 0x1e, KEY_RIGHT }, + { 0x0e, KEY_SELECT }, /* Enter */ + { 0x5a, KEY_INFO }, + { 0x52, KEY_EXIT }, + { 0x59, KEY_PREVIOUS }, + { 0x51, KEY_NEXT }, + { 0x58, KEY_REWIND }, + { 0x50, KEY_FORWARD }, + { 0x44, KEY_PLAYPAUSE }, + { 0x07, KEY_STOP }, + { 0x1b, KEY_RECORD }, + { 0x13, KEY_TUNER }, /* Live */ + { 0x0a, KEY_A }, + { 0x12, KEY_B }, + { 0x03, KEY_PROG1 }, /* 1 */ + { 0x01, KEY_PROG2 }, /* 2 */ + { 0x00, KEY_PROG3 }, /* 3 */ + { 0x06, KEY_DVD }, + { 0x48, KEY_AUX }, /* Photo */ + { 0x40, KEY_VIDEO }, + { 0x19, KEY_AUDIO }, /* Music */ + { 0x0b, KEY_CHANNELUP }, + { 0x08, KEY_CHANNELDOWN }, + { 0x15, KEY_VOLUMEUP }, + { 0x1c, KEY_VOLUMEDOWN }, +}; + +struct ir_scancode_table ir_codes_adstech_dvb_t_pci_table = { + .scan = ir_codes_adstech_dvb_t_pci, + .size = ARRAY_SIZE(ir_codes_adstech_dvb_t_pci), +}; +EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci_table); /* ---------------------------------------------------------------------- */ /* MSI TV@nywhere MASTER remote */ -IR_KEYTAB_TYPE ir_codes_msi_tvanywhere[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_msi_tvanywhere[] = { /* Keys 0 to 9 */ - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0c ] = KEY_MUTE, - [ 0x0f ] = KEY_SCREEN, /* Full Screen */ - [ 0x10 ] = KEY_F, /* Funtion */ - [ 0x11 ] = KEY_T, /* Time shift */ - [ 0x12 ] = KEY_POWER, - [ 0x13 ] = KEY_MEDIA, /* MTS */ - [ 0x14 ] = KEY_SLOW, - [ 0x16 ] = KEY_REWIND, /* backward << */ - [ 0x17 ] = KEY_ENTER, /* Return */ - [ 0x18 ] = KEY_FASTFORWARD, /* forward >> */ - [ 0x1a ] = KEY_CHANNELUP, - [ 0x1b ] = KEY_VOLUMEUP, - [ 0x1e ] = KEY_CHANNELDOWN, - [ 0x1f ] = KEY_VOLUMEDOWN, -}; - -EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere); + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0c, KEY_MUTE }, + { 0x0f, KEY_SCREEN }, /* Full Screen */ + { 0x10, KEY_FN }, /* Funtion */ + { 0x11, KEY_TIME }, /* Time shift */ + { 0x12, KEY_POWER }, + { 0x13, KEY_MEDIA }, /* MTS */ + { 0x14, KEY_SLOW }, + { 0x16, KEY_REWIND }, /* backward << */ + { 0x17, KEY_ENTER }, /* Return */ + { 0x18, KEY_FASTFORWARD }, /* forward >> */ + { 0x1a, KEY_CHANNELUP }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1e, KEY_CHANNELDOWN }, + { 0x1f, KEY_VOLUMEDOWN }, +}; + +struct ir_scancode_table ir_codes_msi_tvanywhere_table = { + .scan = ir_codes_msi_tvanywhere, + .size = ARRAY_SIZE(ir_codes_msi_tvanywhere), +}; +EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_table); /* ---------------------------------------------------------------------- */ @@ -626,7 +668,7 @@ EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere); */ -IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_msi_tvanywhere_plus[] = { /* ---- Remote Button Layout ---- @@ -648,596 +690,645 @@ IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = { << FUNC >> RESET */ - [0x01] = KEY_KP1, /* 1 */ - [0x0b] = KEY_KP2, /* 2 */ - [0x1b] = KEY_KP3, /* 3 */ - [0x05] = KEY_KP4, /* 4 */ - [0x09] = KEY_KP5, /* 5 */ - [0x15] = KEY_KP6, /* 6 */ - [0x06] = KEY_KP7, /* 7 */ - [0x0a] = KEY_KP8, /* 8 */ - [0x12] = KEY_KP9, /* 9 */ - [0x02] = KEY_KP0, /* 0 */ - [0x10] = KEY_KPPLUS, /* + */ - [0x13] = KEY_AGAIN, /* Recall */ - - [0x1e] = KEY_POWER, /* Power */ - [0x07] = KEY_TUNER, /* Source */ - [0x1c] = KEY_SEARCH, /* Scan */ - [0x18] = KEY_MUTE, /* Mute */ - - [0x03] = KEY_RADIO, /* TV/FM */ + { 0x01, KEY_1 }, /* 1 */ + { 0x0b, KEY_2 }, /* 2 */ + { 0x1b, KEY_3 }, /* 3 */ + { 0x05, KEY_4 }, /* 4 */ + { 0x09, KEY_5 }, /* 5 */ + { 0x15, KEY_6 }, /* 6 */ + { 0x06, KEY_7 }, /* 7 */ + { 0x0a, KEY_8 }, /* 8 */ + { 0x12, KEY_9 }, /* 9 */ + { 0x02, KEY_0 }, /* 0 */ + { 0x10, KEY_KPPLUS }, /* + */ + { 0x13, KEY_AGAIN }, /* Recall */ + + { 0x1e, KEY_POWER }, /* Power */ + { 0x07, KEY_TUNER }, /* Source */ + { 0x1c, KEY_SEARCH }, /* Scan */ + { 0x18, KEY_MUTE }, /* Mute */ + + { 0x03, KEY_RADIO }, /* TV/FM */ /* The next four keys are duplicates that appear to send the same IR code as Ch+, Ch-, >>, and << . The raw code assigned to them is the actual code + 0x20 - they will never be detected as such unless some way is discovered to distinguish these buttons from those that have the same code. */ - [0x3f] = KEY_RIGHT, /* |> and Ch+ */ - [0x37] = KEY_LEFT, /* <| and Ch- */ - [0x2c] = KEY_UP, /* ^^Up and >> */ - [0x24] = KEY_DOWN, /* vvDn and << */ - - [0x00] = KEY_RECORD, /* Record */ - [0x08] = KEY_STOP, /* Stop */ - [0x11] = KEY_PLAY, /* Play */ - - [0x0f] = KEY_CLOSE, /* Minimize */ - [0x19] = KEY_ZOOM, /* Zoom */ - [0x1a] = KEY_SHUFFLE, /* Snapshot */ - [0x0d] = KEY_LANGUAGE, /* MTS */ - - [0x14] = KEY_VOLUMEDOWN, /* Vol- */ - [0x16] = KEY_VOLUMEUP, /* Vol+ */ - [0x17] = KEY_CHANNELDOWN, /* Ch- */ - [0x1f] = KEY_CHANNELUP, /* Ch+ */ + { 0x3f, KEY_RIGHT }, /* |> and Ch+ */ + { 0x37, KEY_LEFT }, /* <| and Ch- */ + { 0x2c, KEY_UP }, /* ^^Up and >> */ + { 0x24, KEY_DOWN }, /* vvDn and << */ + + { 0x00, KEY_RECORD }, /* Record */ + { 0x08, KEY_STOP }, /* Stop */ + { 0x11, KEY_PLAY }, /* Play */ + + { 0x0f, KEY_CLOSE }, /* Minimize */ + { 0x19, KEY_ZOOM }, /* Zoom */ + { 0x1a, KEY_CAMERA }, /* Snapshot */ + { 0x0d, KEY_LANGUAGE }, /* MTS */ + + { 0x14, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x16, KEY_VOLUMEUP }, /* Vol+ */ + { 0x17, KEY_CHANNELDOWN }, /* Ch- */ + { 0x1f, KEY_CHANNELUP }, /* Ch+ */ + + { 0x04, KEY_REWIND }, /* << */ + { 0x0e, KEY_MENU }, /* Function */ + { 0x0c, KEY_FASTFORWARD }, /* >> */ + { 0x1d, KEY_RESTART }, /* Reset */ +}; - [0x04] = KEY_REWIND, /* << */ - [0x0e] = KEY_MENU, /* Function */ - [0x0c] = KEY_FASTFORWARD, /* >> */ - [0x1d] = KEY_RESTART, /* Reset */ +struct ir_scancode_table ir_codes_msi_tvanywhere_plus_table = { + .scan = ir_codes_msi_tvanywhere_plus, + .size = ARRAY_SIZE(ir_codes_msi_tvanywhere_plus), }; -EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus); +EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus_table); /* ---------------------------------------------------------------------- */ /* Cinergy 1400 DVB-T */ -IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = { - [ 0x01 ] = KEY_POWER, - [ 0x02 ] = KEY_1, - [ 0x03 ] = KEY_2, - [ 0x04 ] = KEY_3, - [ 0x05 ] = KEY_4, - [ 0x06 ] = KEY_5, - [ 0x07 ] = KEY_6, - [ 0x08 ] = KEY_7, - [ 0x09 ] = KEY_8, - [ 0x0a ] = KEY_9, - [ 0x0c ] = KEY_0, - - [ 0x0b ] = KEY_VIDEO, - [ 0x0d ] = KEY_REFRESH, - [ 0x0e ] = KEY_SELECT, - [ 0x0f ] = KEY_EPG, - [ 0x10 ] = KEY_UP, - [ 0x11 ] = KEY_LEFT, - [ 0x12 ] = KEY_OK, - [ 0x13 ] = KEY_RIGHT, - [ 0x14 ] = KEY_DOWN, - [ 0x15 ] = KEY_TEXT, - [ 0x16 ] = KEY_INFO, - - [ 0x17 ] = KEY_RED, - [ 0x18 ] = KEY_GREEN, - [ 0x19 ] = KEY_YELLOW, - [ 0x1a ] = KEY_BLUE, - - [ 0x1b ] = KEY_CHANNELUP, - [ 0x1c ] = KEY_VOLUMEUP, - [ 0x1d ] = KEY_MUTE, - [ 0x1e ] = KEY_VOLUMEDOWN, - [ 0x1f ] = KEY_CHANNELDOWN, - - [ 0x40 ] = KEY_PAUSE, - [ 0x4c ] = KEY_PLAY, - [ 0x58 ] = KEY_RECORD, - [ 0x54 ] = KEY_PREVIOUS, - [ 0x48 ] = KEY_STOP, - [ 0x5c ] = KEY_NEXT, -}; - -EXPORT_SYMBOL_GPL(ir_codes_cinergy_1400); +static struct ir_scancode ir_codes_cinergy_1400[] = { + { 0x01, KEY_POWER }, + { 0x02, KEY_1 }, + { 0x03, KEY_2 }, + { 0x04, KEY_3 }, + { 0x05, KEY_4 }, + { 0x06, KEY_5 }, + { 0x07, KEY_6 }, + { 0x08, KEY_7 }, + { 0x09, KEY_8 }, + { 0x0a, KEY_9 }, + { 0x0c, KEY_0 }, + + { 0x0b, KEY_VIDEO }, + { 0x0d, KEY_REFRESH }, + { 0x0e, KEY_SELECT }, + { 0x0f, KEY_EPG }, + { 0x10, KEY_UP }, + { 0x11, KEY_LEFT }, + { 0x12, KEY_OK }, + { 0x13, KEY_RIGHT }, + { 0x14, KEY_DOWN }, + { 0x15, KEY_TEXT }, + { 0x16, KEY_INFO }, + + { 0x17, KEY_RED }, + { 0x18, KEY_GREEN }, + { 0x19, KEY_YELLOW }, + { 0x1a, KEY_BLUE }, + + { 0x1b, KEY_CHANNELUP }, + { 0x1c, KEY_VOLUMEUP }, + { 0x1d, KEY_MUTE }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_CHANNELDOWN }, + + { 0x40, KEY_PAUSE }, + { 0x4c, KEY_PLAY }, + { 0x58, KEY_RECORD }, + { 0x54, KEY_PREVIOUS }, + { 0x48, KEY_STOP }, + { 0x5c, KEY_NEXT }, +}; + +struct ir_scancode_table ir_codes_cinergy_1400_table = { + .scan = ir_codes_cinergy_1400, + .size = ARRAY_SIZE(ir_codes_cinergy_1400), +}; +EXPORT_SYMBOL_GPL(ir_codes_cinergy_1400_table); /* ---------------------------------------------------------------------- */ /* AVERTV STUDIO 303 Remote */ -IR_KEYTAB_TYPE ir_codes_avertv_303[IR_KEYTAB_SIZE] = { - [ 0x2a ] = KEY_1, - [ 0x32 ] = KEY_2, - [ 0x3a ] = KEY_3, - [ 0x4a ] = KEY_4, - [ 0x52 ] = KEY_5, - [ 0x5a ] = KEY_6, - [ 0x6a ] = KEY_7, - [ 0x72 ] = KEY_8, - [ 0x7a ] = KEY_9, - [ 0x0e ] = KEY_0, - - [ 0x02 ] = KEY_POWER, - [ 0x22 ] = KEY_VIDEO, - [ 0x42 ] = KEY_AUDIO, - [ 0x62 ] = KEY_ZOOM, - [ 0x0a ] = KEY_TV, - [ 0x12 ] = KEY_CD, - [ 0x1a ] = KEY_TEXT, - - [ 0x16 ] = KEY_SUBTITLE, - [ 0x1e ] = KEY_REWIND, - [ 0x06 ] = KEY_PRINT, - - [ 0x2e ] = KEY_SEARCH, - [ 0x36 ] = KEY_SLEEP, - [ 0x3e ] = KEY_SHUFFLE, - [ 0x26 ] = KEY_MUTE, - - [ 0x4e ] = KEY_RECORD, - [ 0x56 ] = KEY_PAUSE, - [ 0x5e ] = KEY_STOP, - [ 0x46 ] = KEY_PLAY, - - [ 0x6e ] = KEY_RED, - [ 0x0b ] = KEY_GREEN, - [ 0x66 ] = KEY_YELLOW, - [ 0x03 ] = KEY_BLUE, - - [ 0x76 ] = KEY_LEFT, - [ 0x7e ] = KEY_RIGHT, - [ 0x13 ] = KEY_DOWN, - [ 0x1b ] = KEY_UP, -}; - -EXPORT_SYMBOL_GPL(ir_codes_avertv_303); +static struct ir_scancode ir_codes_avertv_303[] = { + { 0x2a, KEY_1 }, + { 0x32, KEY_2 }, + { 0x3a, KEY_3 }, + { 0x4a, KEY_4 }, + { 0x52, KEY_5 }, + { 0x5a, KEY_6 }, + { 0x6a, KEY_7 }, + { 0x72, KEY_8 }, + { 0x7a, KEY_9 }, + { 0x0e, KEY_0 }, + + { 0x02, KEY_POWER }, + { 0x22, KEY_VIDEO }, + { 0x42, KEY_AUDIO }, + { 0x62, KEY_ZOOM }, + { 0x0a, KEY_TV }, + { 0x12, KEY_CD }, + { 0x1a, KEY_TEXT }, + + { 0x16, KEY_SUBTITLE }, + { 0x1e, KEY_REWIND }, + { 0x06, KEY_PRINT }, + + { 0x2e, KEY_SEARCH }, + { 0x36, KEY_SLEEP }, + { 0x3e, KEY_SHUFFLE }, + { 0x26, KEY_MUTE }, + + { 0x4e, KEY_RECORD }, + { 0x56, KEY_PAUSE }, + { 0x5e, KEY_STOP }, + { 0x46, KEY_PLAY }, + + { 0x6e, KEY_RED }, + { 0x0b, KEY_GREEN }, + { 0x66, KEY_YELLOW }, + { 0x03, KEY_BLUE }, + + { 0x76, KEY_LEFT }, + { 0x7e, KEY_RIGHT }, + { 0x13, KEY_DOWN }, + { 0x1b, KEY_UP }, +}; + +struct ir_scancode_table ir_codes_avertv_303_table = { + .scan = ir_codes_avertv_303, + .size = ARRAY_SIZE(ir_codes_avertv_303), +}; +EXPORT_SYMBOL_GPL(ir_codes_avertv_303_table); /* ---------------------------------------------------------------------- */ /* DigitalNow DNTV Live! DVB-T Pro Remote */ -IR_KEYTAB_TYPE ir_codes_dntv_live_dvbt_pro[IR_KEYTAB_SIZE] = { - [ 0x16 ] = KEY_POWER, - [ 0x5b ] = KEY_HOME, - - [ 0x55 ] = KEY_TV, /* live tv */ - [ 0x58 ] = KEY_TUNER, /* digital Radio */ - [ 0x5a ] = KEY_RADIO, /* FM radio */ - [ 0x59 ] = KEY_DVD, /* dvd menu */ - [ 0x03 ] = KEY_1, - [ 0x01 ] = KEY_2, - [ 0x06 ] = KEY_3, - [ 0x09 ] = KEY_4, - [ 0x1d ] = KEY_5, - [ 0x1f ] = KEY_6, - [ 0x0d ] = KEY_7, - [ 0x19 ] = KEY_8, - [ 0x1b ] = KEY_9, - [ 0x0c ] = KEY_CANCEL, - [ 0x15 ] = KEY_0, - [ 0x4a ] = KEY_CLEAR, - [ 0x13 ] = KEY_BACK, - [ 0x00 ] = KEY_TAB, - [ 0x4b ] = KEY_UP, - [ 0x4e ] = KEY_LEFT, - [ 0x4f ] = KEY_OK, - [ 0x52 ] = KEY_RIGHT, - [ 0x51 ] = KEY_DOWN, - [ 0x1e ] = KEY_VOLUMEUP, - [ 0x0a ] = KEY_VOLUMEDOWN, - [ 0x02 ] = KEY_CHANNELDOWN, - [ 0x05 ] = KEY_CHANNELUP, - [ 0x11 ] = KEY_RECORD, - [ 0x14 ] = KEY_PLAY, - [ 0x4c ] = KEY_PAUSE, - [ 0x1a ] = KEY_STOP, - [ 0x40 ] = KEY_REWIND, - [ 0x12 ] = KEY_FASTFORWARD, - [ 0x41 ] = KEY_PREVIOUSSONG, /* replay |< */ - [ 0x42 ] = KEY_NEXTSONG, /* skip >| */ - [ 0x54 ] = KEY_CAMERA, /* capture */ - [ 0x50 ] = KEY_LANGUAGE, /* sap */ - [ 0x47 ] = KEY_TV2, /* pip */ - [ 0x4d ] = KEY_SCREEN, - [ 0x43 ] = KEY_SUBTITLE, - [ 0x10 ] = KEY_MUTE, - [ 0x49 ] = KEY_AUDIO, /* l/r */ - [ 0x07 ] = KEY_SLEEP, - [ 0x08 ] = KEY_VIDEO, /* a/v */ - [ 0x0e ] = KEY_PREVIOUS, /* recall */ - [ 0x45 ] = KEY_ZOOM, /* zoom + */ - [ 0x46 ] = KEY_ANGLE, /* zoom - */ - [ 0x56 ] = KEY_RED, - [ 0x57 ] = KEY_GREEN, - [ 0x5c ] = KEY_YELLOW, - [ 0x5d ] = KEY_BLUE, -}; - -EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvbt_pro); - -IR_KEYTAB_TYPE ir_codes_em_terratec[IR_KEYTAB_SIZE] = { - [ 0x01 ] = KEY_CHANNEL, - [ 0x02 ] = KEY_SELECT, - [ 0x03 ] = KEY_MUTE, - [ 0x04 ] = KEY_POWER, - [ 0x05 ] = KEY_1, - [ 0x06 ] = KEY_2, - [ 0x07 ] = KEY_3, - [ 0x08 ] = KEY_CHANNELUP, - [ 0x09 ] = KEY_4, - [ 0x0a ] = KEY_5, - [ 0x0b ] = KEY_6, - [ 0x0c ] = KEY_CHANNELDOWN, - [ 0x0d ] = KEY_7, - [ 0x0e ] = KEY_8, - [ 0x0f ] = KEY_9, - [ 0x10 ] = KEY_VOLUMEUP, - [ 0x11 ] = KEY_0, - [ 0x12 ] = KEY_MENU, - [ 0x13 ] = KEY_PRINT, - [ 0x14 ] = KEY_VOLUMEDOWN, - [ 0x16 ] = KEY_PAUSE, - [ 0x18 ] = KEY_RECORD, - [ 0x19 ] = KEY_REWIND, - [ 0x1a ] = KEY_PLAY, - [ 0x1b ] = KEY_FORWARD, - [ 0x1c ] = KEY_BACKSPACE, - [ 0x1e ] = KEY_STOP, - [ 0x40 ] = KEY_ZOOM, -}; - -EXPORT_SYMBOL_GPL(ir_codes_em_terratec); - -IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = { - [ 0x3a ] = KEY_0, - [ 0x31 ] = KEY_1, - [ 0x32 ] = KEY_2, - [ 0x33 ] = KEY_3, - [ 0x34 ] = KEY_4, - [ 0x35 ] = KEY_5, - [ 0x36 ] = KEY_6, - [ 0x37 ] = KEY_7, - [ 0x38 ] = KEY_8, - [ 0x39 ] = KEY_9, - - [ 0x2f ] = KEY_POWER, - - [ 0x2e ] = KEY_P, - [ 0x1f ] = KEY_L, - [ 0x2b ] = KEY_I, - - [ 0x2d ] = KEY_SCREEN, - [ 0x1e ] = KEY_ZOOM, - [ 0x1b ] = KEY_VOLUMEUP, - [ 0x0f ] = KEY_VOLUMEDOWN, - [ 0x17 ] = KEY_CHANNELUP, - [ 0x1c ] = KEY_CHANNELDOWN, - [ 0x25 ] = KEY_INFO, - - [ 0x3c ] = KEY_MUTE, - - [ 0x3d ] = KEY_LEFT, - [ 0x3b ] = KEY_RIGHT, - - [ 0x3f ] = KEY_UP, - [ 0x3e ] = KEY_DOWN, - [ 0x1a ] = KEY_ENTER, - - [ 0x1d ] = KEY_MENU, - [ 0x19 ] = KEY_AGAIN, - [ 0x16 ] = KEY_PREVIOUSSONG, - [ 0x13 ] = KEY_NEXTSONG, - [ 0x15 ] = KEY_PAUSE, - [ 0x0e ] = KEY_REWIND, - [ 0x0d ] = KEY_PLAY, - [ 0x0b ] = KEY_STOP, - [ 0x07 ] = KEY_FORWARD, - [ 0x27 ] = KEY_RECORD, - [ 0x26 ] = KEY_TUNER, - [ 0x29 ] = KEY_TEXT, - [ 0x2a ] = KEY_MEDIA, - [ 0x18 ] = KEY_EPG, -}; - -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_grey); - -IR_KEYTAB_TYPE ir_codes_flyvideo[IR_KEYTAB_SIZE] = { - [ 0x0f ] = KEY_0, - [ 0x03 ] = KEY_1, - [ 0x04 ] = KEY_2, - [ 0x05 ] = KEY_3, - [ 0x07 ] = KEY_4, - [ 0x08 ] = KEY_5, - [ 0x09 ] = KEY_6, - [ 0x0b ] = KEY_7, - [ 0x0c ] = KEY_8, - [ 0x0d ] = KEY_9, - - [ 0x0e ] = KEY_MODE, // Air/Cable - [ 0x11 ] = KEY_VIDEO, // Video - [ 0x15 ] = KEY_AUDIO, // Audio - [ 0x00 ] = KEY_POWER, // Power - [ 0x18 ] = KEY_TUNER, // AV Source - [ 0x02 ] = KEY_ZOOM, // Fullscreen - [ 0x1a ] = KEY_LANGUAGE, // Stereo - [ 0x1b ] = KEY_MUTE, // Mute - [ 0x14 ] = KEY_VOLUMEUP, // Volume + - [ 0x17 ] = KEY_VOLUMEDOWN, // Volume - - [ 0x12 ] = KEY_CHANNELUP, // Channel + - [ 0x13 ] = KEY_CHANNELDOWN, // Channel - - [ 0x06 ] = KEY_AGAIN, // Recall - [ 0x10 ] = KEY_ENTER, // Enter - - [ 0x19 ] = KEY_BACK, // Rewind ( <<< ) - [ 0x1f ] = KEY_FORWARD, // Forward ( >>> ) - [ 0x0a ] = KEY_ANGLE, // (no label, may be used as the PAUSE button) -}; - -EXPORT_SYMBOL_GPL(ir_codes_flyvideo); - -IR_KEYTAB_TYPE ir_codes_flydvb[IR_KEYTAB_SIZE] = { - [ 0x01 ] = KEY_ZOOM, // Full Screen - [ 0x00 ] = KEY_POWER, // Power - - [ 0x03 ] = KEY_1, - [ 0x04 ] = KEY_2, - [ 0x05 ] = KEY_3, - [ 0x07 ] = KEY_4, - [ 0x08 ] = KEY_5, - [ 0x09 ] = KEY_6, - [ 0x0b ] = KEY_7, - [ 0x0c ] = KEY_8, - [ 0x0d ] = KEY_9, - [ 0x06 ] = KEY_AGAIN, // Recall - [ 0x0f ] = KEY_0, - [ 0x10 ] = KEY_MUTE, // Mute - [ 0x02 ] = KEY_RADIO, // TV/Radio - [ 0x1b ] = KEY_LANGUAGE, // SAP (Second Audio Program) - - [ 0x14 ] = KEY_VOLUMEUP, // VOL+ - [ 0x17 ] = KEY_VOLUMEDOWN, // VOL- - [ 0x12 ] = KEY_CHANNELUP, // CH+ - [ 0x13 ] = KEY_CHANNELDOWN, // CH- - [ 0x1d ] = KEY_ENTER, // Enter - - [ 0x1a ] = KEY_MODE, // PIP - [ 0x18 ] = KEY_TUNER, // Source - - [ 0x1e ] = KEY_RECORD, // Record/Pause - [ 0x15 ] = KEY_ANGLE, // Swap (no label on key) - [ 0x1c ] = KEY_PAUSE, // Timeshift/Pause - [ 0x19 ] = KEY_BACK, // Rewind << - [ 0x0a ] = KEY_PLAYPAUSE, // Play/Pause - [ 0x1f ] = KEY_FORWARD, // Forward >> - [ 0x16 ] = KEY_PREVIOUS, // Back |<< - [ 0x11 ] = KEY_STOP, // Stop - [ 0x0e ] = KEY_NEXT, // End >>| -}; - -EXPORT_SYMBOL_GPL(ir_codes_flydvb); - -IR_KEYTAB_TYPE ir_codes_cinergy[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0a ] = KEY_POWER, - [ 0x0b ] = KEY_PROG1, // app - [ 0x0c ] = KEY_ZOOM, // zoom/fullscreen - [ 0x0d ] = KEY_CHANNELUP, // channel - [ 0x0e ] = KEY_CHANNELDOWN, // channel- - [ 0x0f ] = KEY_VOLUMEUP, - [ 0x10 ] = KEY_VOLUMEDOWN, - [ 0x11 ] = KEY_TUNER, // AV - [ 0x12 ] = KEY_NUMLOCK, // -/-- - [ 0x13 ] = KEY_AUDIO, // audio - [ 0x14 ] = KEY_MUTE, - [ 0x15 ] = KEY_UP, - [ 0x16 ] = KEY_DOWN, - [ 0x17 ] = KEY_LEFT, - [ 0x18 ] = KEY_RIGHT, - [ 0x19 ] = BTN_LEFT, - [ 0x1a ] = BTN_RIGHT, - [ 0x1b ] = KEY_WWW, // text - [ 0x1c ] = KEY_REWIND, - [ 0x1d ] = KEY_FORWARD, - [ 0x1e ] = KEY_RECORD, - [ 0x1f ] = KEY_PLAY, - [ 0x20 ] = KEY_PREVIOUSSONG, - [ 0x21 ] = KEY_NEXTSONG, - [ 0x22 ] = KEY_PAUSE, - [ 0x23 ] = KEY_STOP, -}; - -EXPORT_SYMBOL_GPL(ir_codes_cinergy); +static struct ir_scancode ir_codes_dntv_live_dvbt_pro[] = { + { 0x16, KEY_POWER }, + { 0x5b, KEY_HOME }, + + { 0x55, KEY_TV }, /* live tv */ + { 0x58, KEY_TUNER }, /* digital Radio */ + { 0x5a, KEY_RADIO }, /* FM radio */ + { 0x59, KEY_DVD }, /* dvd menu */ + { 0x03, KEY_1 }, + { 0x01, KEY_2 }, + { 0x06, KEY_3 }, + { 0x09, KEY_4 }, + { 0x1d, KEY_5 }, + { 0x1f, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x19, KEY_8 }, + { 0x1b, KEY_9 }, + { 0x0c, KEY_CANCEL }, + { 0x15, KEY_0 }, + { 0x4a, KEY_CLEAR }, + { 0x13, KEY_BACK }, + { 0x00, KEY_TAB }, + { 0x4b, KEY_UP }, + { 0x4e, KEY_LEFT }, + { 0x4f, KEY_OK }, + { 0x52, KEY_RIGHT }, + { 0x51, KEY_DOWN }, + { 0x1e, KEY_VOLUMEUP }, + { 0x0a, KEY_VOLUMEDOWN }, + { 0x02, KEY_CHANNELDOWN }, + { 0x05, KEY_CHANNELUP }, + { 0x11, KEY_RECORD }, + { 0x14, KEY_PLAY }, + { 0x4c, KEY_PAUSE }, + { 0x1a, KEY_STOP }, + { 0x40, KEY_REWIND }, + { 0x12, KEY_FASTFORWARD }, + { 0x41, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x42, KEY_NEXTSONG }, /* skip >| */ + { 0x54, KEY_CAMERA }, /* capture */ + { 0x50, KEY_LANGUAGE }, /* sap */ + { 0x47, KEY_TV2 }, /* pip */ + { 0x4d, KEY_SCREEN }, + { 0x43, KEY_SUBTITLE }, + { 0x10, KEY_MUTE }, + { 0x49, KEY_AUDIO }, /* l/r */ + { 0x07, KEY_SLEEP }, + { 0x08, KEY_VIDEO }, /* a/v */ + { 0x0e, KEY_PREVIOUS }, /* recall */ + { 0x45, KEY_ZOOM }, /* zoom + */ + { 0x46, KEY_ANGLE }, /* zoom - */ + { 0x56, KEY_RED }, + { 0x57, KEY_GREEN }, + { 0x5c, KEY_YELLOW }, + { 0x5d, KEY_BLUE }, +}; + +struct ir_scancode_table ir_codes_dntv_live_dvbt_pro_table = { + .scan = ir_codes_dntv_live_dvbt_pro, + .size = ARRAY_SIZE(ir_codes_dntv_live_dvbt_pro), +}; +EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvbt_pro_table); + +static struct ir_scancode ir_codes_em_terratec[] = { + { 0x01, KEY_CHANNEL }, + { 0x02, KEY_SELECT }, + { 0x03, KEY_MUTE }, + { 0x04, KEY_POWER }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x08, KEY_CHANNELUP }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0c, KEY_CHANNELDOWN }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_0 }, + { 0x12, KEY_MENU }, + { 0x13, KEY_PRINT }, + { 0x14, KEY_VOLUMEDOWN }, + { 0x16, KEY_PAUSE }, + { 0x18, KEY_RECORD }, + { 0x19, KEY_REWIND }, + { 0x1a, KEY_PLAY }, + { 0x1b, KEY_FORWARD }, + { 0x1c, KEY_BACKSPACE }, + { 0x1e, KEY_STOP }, + { 0x40, KEY_ZOOM }, +}; + +struct ir_scancode_table ir_codes_em_terratec_table = { + .scan = ir_codes_em_terratec, + .size = ARRAY_SIZE(ir_codes_em_terratec), +}; +EXPORT_SYMBOL_GPL(ir_codes_em_terratec_table); + +static struct ir_scancode ir_codes_pinnacle_grey[] = { + { 0x3a, KEY_0 }, + { 0x31, KEY_1 }, + { 0x32, KEY_2 }, + { 0x33, KEY_3 }, + { 0x34, KEY_4 }, + { 0x35, KEY_5 }, + { 0x36, KEY_6 }, + { 0x37, KEY_7 }, + { 0x38, KEY_8 }, + { 0x39, KEY_9 }, + + { 0x2f, KEY_POWER }, + + { 0x2e, KEY_P }, + { 0x1f, KEY_L }, + { 0x2b, KEY_I }, + + { 0x2d, KEY_SCREEN }, + { 0x1e, KEY_ZOOM }, + { 0x1b, KEY_VOLUMEUP }, + { 0x0f, KEY_VOLUMEDOWN }, + { 0x17, KEY_CHANNELUP }, + { 0x1c, KEY_CHANNELDOWN }, + { 0x25, KEY_INFO }, + + { 0x3c, KEY_MUTE }, + + { 0x3d, KEY_LEFT }, + { 0x3b, KEY_RIGHT }, + + { 0x3f, KEY_UP }, + { 0x3e, KEY_DOWN }, + { 0x1a, KEY_ENTER }, + + { 0x1d, KEY_MENU }, + { 0x19, KEY_AGAIN }, + { 0x16, KEY_PREVIOUSSONG }, + { 0x13, KEY_NEXTSONG }, + { 0x15, KEY_PAUSE }, + { 0x0e, KEY_REWIND }, + { 0x0d, KEY_PLAY }, + { 0x0b, KEY_STOP }, + { 0x07, KEY_FORWARD }, + { 0x27, KEY_RECORD }, + { 0x26, KEY_TUNER }, + { 0x29, KEY_TEXT }, + { 0x2a, KEY_MEDIA }, + { 0x18, KEY_EPG }, +}; + +struct ir_scancode_table ir_codes_pinnacle_grey_table = { + .scan = ir_codes_pinnacle_grey, + .size = ARRAY_SIZE(ir_codes_pinnacle_grey), +}; +EXPORT_SYMBOL_GPL(ir_codes_pinnacle_grey_table); + +static struct ir_scancode ir_codes_flyvideo[] = { + { 0x0f, KEY_0 }, + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x07, KEY_4 }, + { 0x08, KEY_5 }, + { 0x09, KEY_6 }, + { 0x0b, KEY_7 }, + { 0x0c, KEY_8 }, + { 0x0d, KEY_9 }, + + { 0x0e, KEY_MODE }, /* Air/Cable */ + { 0x11, KEY_VIDEO }, /* Video */ + { 0x15, KEY_AUDIO }, /* Audio */ + { 0x00, KEY_POWER }, /* Power */ + { 0x18, KEY_TUNER }, /* AV Source */ + { 0x02, KEY_ZOOM }, /* Fullscreen */ + { 0x1a, KEY_LANGUAGE }, /* Stereo */ + { 0x1b, KEY_MUTE }, /* Mute */ + { 0x14, KEY_VOLUMEUP }, /* Volume + */ + { 0x17, KEY_VOLUMEDOWN },/* Volume - */ + { 0x12, KEY_CHANNELUP },/* Channel + */ + { 0x13, KEY_CHANNELDOWN },/* Channel - */ + { 0x06, KEY_AGAIN }, /* Recall */ + { 0x10, KEY_ENTER }, /* Enter */ + + { 0x19, KEY_BACK }, /* Rewind ( <<< ) */ + { 0x1f, KEY_FORWARD }, /* Forward ( >>> ) */ + { 0x0a, KEY_ANGLE }, /* no label, may be used as the PAUSE button */ +}; + +struct ir_scancode_table ir_codes_flyvideo_table = { + .scan = ir_codes_flyvideo, + .size = ARRAY_SIZE(ir_codes_flyvideo), +}; +EXPORT_SYMBOL_GPL(ir_codes_flyvideo_table); + +static struct ir_scancode ir_codes_flydvb[] = { + { 0x01, KEY_ZOOM }, /* Full Screen */ + { 0x00, KEY_POWER }, /* Power */ + + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x07, KEY_4 }, + { 0x08, KEY_5 }, + { 0x09, KEY_6 }, + { 0x0b, KEY_7 }, + { 0x0c, KEY_8 }, + { 0x0d, KEY_9 }, + { 0x06, KEY_AGAIN }, /* Recall */ + { 0x0f, KEY_0 }, + { 0x10, KEY_MUTE }, /* Mute */ + { 0x02, KEY_RADIO }, /* TV/Radio */ + { 0x1b, KEY_LANGUAGE }, /* SAP (Second Audio Program) */ + + { 0x14, KEY_VOLUMEUP }, /* VOL+ */ + { 0x17, KEY_VOLUMEDOWN }, /* VOL- */ + { 0x12, KEY_CHANNELUP }, /* CH+ */ + { 0x13, KEY_CHANNELDOWN }, /* CH- */ + { 0x1d, KEY_ENTER }, /* Enter */ + + { 0x1a, KEY_MODE }, /* PIP */ + { 0x18, KEY_TUNER }, /* Source */ + + { 0x1e, KEY_RECORD }, /* Record/Pause */ + { 0x15, KEY_ANGLE }, /* Swap (no label on key) */ + { 0x1c, KEY_PAUSE }, /* Timeshift/Pause */ + { 0x19, KEY_BACK }, /* Rewind << */ + { 0x0a, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x1f, KEY_FORWARD }, /* Forward >> */ + { 0x16, KEY_PREVIOUS }, /* Back |<< */ + { 0x11, KEY_STOP }, /* Stop */ + { 0x0e, KEY_NEXT }, /* End >>| */ +}; + +struct ir_scancode_table ir_codes_flydvb_table = { + .scan = ir_codes_flydvb, + .size = ARRAY_SIZE(ir_codes_flydvb), +}; +EXPORT_SYMBOL_GPL(ir_codes_flydvb_table); + +static struct ir_scancode ir_codes_cinergy[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_POWER }, + { 0x0b, KEY_PROG1 }, /* app */ + { 0x0c, KEY_ZOOM }, /* zoom/fullscreen */ + { 0x0d, KEY_CHANNELUP }, /* channel */ + { 0x0e, KEY_CHANNELDOWN }, /* channel- */ + { 0x0f, KEY_VOLUMEUP }, + { 0x10, KEY_VOLUMEDOWN }, + { 0x11, KEY_TUNER }, /* AV */ + { 0x12, KEY_NUMLOCK }, /* -/-- */ + { 0x13, KEY_AUDIO }, /* audio */ + { 0x14, KEY_MUTE }, + { 0x15, KEY_UP }, + { 0x16, KEY_DOWN }, + { 0x17, KEY_LEFT }, + { 0x18, KEY_RIGHT }, + { 0x19, BTN_LEFT, }, + { 0x1a, BTN_RIGHT, }, + { 0x1b, KEY_WWW }, /* text */ + { 0x1c, KEY_REWIND }, + { 0x1d, KEY_FORWARD }, + { 0x1e, KEY_RECORD }, + { 0x1f, KEY_PLAY }, + { 0x20, KEY_PREVIOUSSONG }, + { 0x21, KEY_NEXTSONG }, + { 0x22, KEY_PAUSE }, + { 0x23, KEY_STOP }, +}; + +struct ir_scancode_table ir_codes_cinergy_table = { + .scan = ir_codes_cinergy, + .size = ARRAY_SIZE(ir_codes_cinergy), +}; +EXPORT_SYMBOL_GPL(ir_codes_cinergy_table); /* Alfons Geser <a.geser@cox.net> * updates from Job D. R. Borges <jobdrb@ig.com.br> */ -IR_KEYTAB_TYPE ir_codes_eztv[IR_KEYTAB_SIZE] = { - [ 0x12 ] = KEY_POWER, - [ 0x01 ] = KEY_TV, // DVR - [ 0x15 ] = KEY_DVD, // DVD - [ 0x17 ] = KEY_AUDIO, // music - // DVR mode / DVD mode / music mode - - [ 0x1b ] = KEY_MUTE, // mute - [ 0x02 ] = KEY_LANGUAGE, // MTS/SAP / audio / autoseek - [ 0x1e ] = KEY_SUBTITLE, // closed captioning / subtitle / seek - [ 0x16 ] = KEY_ZOOM, // full screen - [ 0x1c ] = KEY_VIDEO, // video source / eject / delall - [ 0x1d ] = KEY_RESTART, // playback / angle / del - [ 0x2f ] = KEY_SEARCH, // scan / menu / playlist - [ 0x30 ] = KEY_CHANNEL, // CH surfing / bookmark / memo - - [ 0x31 ] = KEY_HELP, // help - [ 0x32 ] = KEY_MODE, // num/memo - [ 0x33 ] = KEY_ESC, // cancel - - [ 0x0c ] = KEY_UP, // up - [ 0x10 ] = KEY_DOWN, // down - [ 0x08 ] = KEY_LEFT, // left - [ 0x04 ] = KEY_RIGHT, // right - [ 0x03 ] = KEY_SELECT, // select - - [ 0x1f ] = KEY_REWIND, // rewind - [ 0x20 ] = KEY_PLAYPAUSE, // play/pause - [ 0x29 ] = KEY_FORWARD, // forward - [ 0x14 ] = KEY_AGAIN, // repeat - [ 0x2b ] = KEY_RECORD, // recording - [ 0x2c ] = KEY_STOP, // stop - [ 0x2d ] = KEY_PLAY, // play - [ 0x2e ] = KEY_SHUFFLE, // snapshot / shuffle - - [ 0x00 ] = KEY_0, - [ 0x05 ] = KEY_1, - [ 0x06 ] = KEY_2, - [ 0x07 ] = KEY_3, - [ 0x09 ] = KEY_4, - [ 0x0a ] = KEY_5, - [ 0x0b ] = KEY_6, - [ 0x0d ] = KEY_7, - [ 0x0e ] = KEY_8, - [ 0x0f ] = KEY_9, - - [ 0x2a ] = KEY_VOLUMEUP, - [ 0x11 ] = KEY_VOLUMEDOWN, - [ 0x18 ] = KEY_CHANNELUP, // CH.tracking up - [ 0x19 ] = KEY_CHANNELDOWN, // CH.tracking down - - [ 0x13 ] = KEY_ENTER, // enter - [ 0x21 ] = KEY_DOT, // . (decimal dot) -}; - -EXPORT_SYMBOL_GPL(ir_codes_eztv); +static struct ir_scancode ir_codes_eztv[] = { + { 0x12, KEY_POWER }, + { 0x01, KEY_TV }, /* DVR */ + { 0x15, KEY_DVD }, /* DVD */ + { 0x17, KEY_AUDIO }, /* music */ + /* DVR mode / DVD mode / music mode */ + + { 0x1b, KEY_MUTE }, /* mute */ + { 0x02, KEY_LANGUAGE }, /* MTS/SAP / audio / autoseek */ + { 0x1e, KEY_SUBTITLE }, /* closed captioning / subtitle / seek */ + { 0x16, KEY_ZOOM }, /* full screen */ + { 0x1c, KEY_VIDEO }, /* video source / eject / delall */ + { 0x1d, KEY_RESTART }, /* playback / angle / del */ + { 0x2f, KEY_SEARCH }, /* scan / menu / playlist */ + { 0x30, KEY_CHANNEL }, /* CH surfing / bookmark / memo */ + + { 0x31, KEY_HELP }, /* help */ + { 0x32, KEY_MODE }, /* num/memo */ + { 0x33, KEY_ESC }, /* cancel */ + + { 0x0c, KEY_UP }, /* up */ + { 0x10, KEY_DOWN }, /* down */ + { 0x08, KEY_LEFT }, /* left */ + { 0x04, KEY_RIGHT }, /* right */ + { 0x03, KEY_SELECT }, /* select */ + + { 0x1f, KEY_REWIND }, /* rewind */ + { 0x20, KEY_PLAYPAUSE },/* play/pause */ + { 0x29, KEY_FORWARD }, /* forward */ + { 0x14, KEY_AGAIN }, /* repeat */ + { 0x2b, KEY_RECORD }, /* recording */ + { 0x2c, KEY_STOP }, /* stop */ + { 0x2d, KEY_PLAY }, /* play */ + { 0x2e, KEY_CAMERA }, /* snapshot / shuffle */ + + { 0x00, KEY_0 }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + + { 0x2a, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x18, KEY_CHANNELUP },/* CH.tracking up */ + { 0x19, KEY_CHANNELDOWN },/* CH.tracking down */ + + { 0x13, KEY_ENTER }, /* enter */ + { 0x21, KEY_DOT }, /* . (decimal dot) */ +}; + +struct ir_scancode_table ir_codes_eztv_table = { + .scan = ir_codes_eztv, + .size = ARRAY_SIZE(ir_codes_eztv), +}; +EXPORT_SYMBOL_GPL(ir_codes_eztv_table); /* Alex Hermann <gaaf@gmx.net> */ -IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE] = { - [ 0x28 ] = KEY_1, - [ 0x18 ] = KEY_2, - [ 0x38 ] = KEY_3, - [ 0x24 ] = KEY_4, - [ 0x14 ] = KEY_5, - [ 0x34 ] = KEY_6, - [ 0x2c ] = KEY_7, - [ 0x1c ] = KEY_8, - [ 0x3c ] = KEY_9, - [ 0x22 ] = KEY_0, - - [ 0x20 ] = KEY_TV, /* TV/FM */ - [ 0x10 ] = KEY_CD, /* CD */ - [ 0x30 ] = KEY_TEXT, /* TELETEXT */ - [ 0x00 ] = KEY_POWER, /* POWER */ - - [ 0x08 ] = KEY_VIDEO, /* VIDEO */ - [ 0x04 ] = KEY_AUDIO, /* AUDIO */ - [ 0x0c ] = KEY_ZOOM, /* FULL SCREEN */ - - [ 0x12 ] = KEY_SUBTITLE, /* DISPLAY */ - [ 0x32 ] = KEY_REWIND, /* LOOP */ - [ 0x02 ] = KEY_PRINT, /* PREVIEW */ - - [ 0x2a ] = KEY_SEARCH, /* AUTOSCAN */ - [ 0x1a ] = KEY_SLEEP, /* FREEZE */ - [ 0x3a ] = KEY_SHUFFLE, /* SNAPSHOT */ - [ 0x0a ] = KEY_MUTE, /* MUTE */ - - [ 0x26 ] = KEY_RECORD, /* RECORD */ - [ 0x16 ] = KEY_PAUSE, /* PAUSE */ - [ 0x36 ] = KEY_STOP, /* STOP */ - [ 0x06 ] = KEY_PLAY, /* PLAY */ - - [ 0x2e ] = KEY_RED, /* RED */ - [ 0x21 ] = KEY_GREEN, /* GREEN */ - [ 0x0e ] = KEY_YELLOW, /* YELLOW */ - [ 0x01 ] = KEY_BLUE, /* BLUE */ - - [ 0x1e ] = KEY_VOLUMEDOWN, /* VOLUME- */ - [ 0x3e ] = KEY_VOLUMEUP, /* VOLUME+ */ - [ 0x11 ] = KEY_CHANNELDOWN, /* CHANNEL/PAGE- */ - [ 0x31 ] = KEY_CHANNELUP /* CHANNEL/PAGE+ */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_avermedia); - -IR_KEYTAB_TYPE ir_codes_videomate_tv_pvr[IR_KEYTAB_SIZE] = { - [ 0x14 ] = KEY_MUTE, - [ 0x24 ] = KEY_ZOOM, - - [ 0x01 ] = KEY_DVD, - [ 0x23 ] = KEY_RADIO, - [ 0x00 ] = KEY_TV, - - [ 0x0a ] = KEY_REWIND, - [ 0x08 ] = KEY_PLAYPAUSE, - [ 0x0f ] = KEY_FORWARD, - - [ 0x02 ] = KEY_PREVIOUS, - [ 0x07 ] = KEY_STOP, - [ 0x06 ] = KEY_NEXT, - - [ 0x0c ] = KEY_UP, - [ 0x0e ] = KEY_DOWN, - [ 0x0b ] = KEY_LEFT, - [ 0x0d ] = KEY_RIGHT, - [ 0x11 ] = KEY_OK, - - [ 0x03 ] = KEY_MENU, - [ 0x09 ] = KEY_SETUP, - [ 0x05 ] = KEY_VIDEO, - [ 0x22 ] = KEY_CHANNEL, - - [ 0x12 ] = KEY_VOLUMEUP, - [ 0x15 ] = KEY_VOLUMEDOWN, - [ 0x10 ] = KEY_CHANNELUP, - [ 0x13 ] = KEY_CHANNELDOWN, - - [ 0x04 ] = KEY_RECORD, - - [ 0x16 ] = KEY_1, - [ 0x17 ] = KEY_2, - [ 0x18 ] = KEY_3, - [ 0x19 ] = KEY_4, - [ 0x1a ] = KEY_5, - [ 0x1b ] = KEY_6, - [ 0x1c ] = KEY_7, - [ 0x1d ] = KEY_8, - [ 0x1e ] = KEY_9, - [ 0x1f ] = KEY_0, - - [ 0x20 ] = KEY_LANGUAGE, - [ 0x21 ] = KEY_SLEEP, -}; - -EXPORT_SYMBOL_GPL(ir_codes_videomate_tv_pvr); +static struct ir_scancode ir_codes_avermedia[] = { + { 0x28, KEY_1 }, + { 0x18, KEY_2 }, + { 0x38, KEY_3 }, + { 0x24, KEY_4 }, + { 0x14, KEY_5 }, + { 0x34, KEY_6 }, + { 0x2c, KEY_7 }, + { 0x1c, KEY_8 }, + { 0x3c, KEY_9 }, + { 0x22, KEY_0 }, + + { 0x20, KEY_TV }, /* TV/FM */ + { 0x10, KEY_CD }, /* CD */ + { 0x30, KEY_TEXT }, /* TELETEXT */ + { 0x00, KEY_POWER }, /* POWER */ + + { 0x08, KEY_VIDEO }, /* VIDEO */ + { 0x04, KEY_AUDIO }, /* AUDIO */ + { 0x0c, KEY_ZOOM }, /* FULL SCREEN */ + + { 0x12, KEY_SUBTITLE }, /* DISPLAY */ + { 0x32, KEY_REWIND }, /* LOOP */ + { 0x02, KEY_PRINT }, /* PREVIEW */ + + { 0x2a, KEY_SEARCH }, /* AUTOSCAN */ + { 0x1a, KEY_SLEEP }, /* FREEZE */ + { 0x3a, KEY_CAMERA }, /* SNAPSHOT */ + { 0x0a, KEY_MUTE }, /* MUTE */ + + { 0x26, KEY_RECORD }, /* RECORD */ + { 0x16, KEY_PAUSE }, /* PAUSE */ + { 0x36, KEY_STOP }, /* STOP */ + { 0x06, KEY_PLAY }, /* PLAY */ + + { 0x2e, KEY_RED }, /* RED */ + { 0x21, KEY_GREEN }, /* GREEN */ + { 0x0e, KEY_YELLOW }, /* YELLOW */ + { 0x01, KEY_BLUE }, /* BLUE */ + + { 0x1e, KEY_VOLUMEDOWN }, /* VOLUME- */ + { 0x3e, KEY_VOLUMEUP }, /* VOLUME+ */ + { 0x11, KEY_CHANNELDOWN }, /* CHANNEL/PAGE- */ + { 0x31, KEY_CHANNELUP } /* CHANNEL/PAGE+ */ +}; + +struct ir_scancode_table ir_codes_avermedia_table = { + .scan = ir_codes_avermedia, + .size = ARRAY_SIZE(ir_codes_avermedia), +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_table); + +static struct ir_scancode ir_codes_videomate_tv_pvr[] = { + { 0x14, KEY_MUTE }, + { 0x24, KEY_ZOOM }, + + { 0x01, KEY_DVD }, + { 0x23, KEY_RADIO }, + { 0x00, KEY_TV }, + + { 0x0a, KEY_REWIND }, + { 0x08, KEY_PLAYPAUSE }, + { 0x0f, KEY_FORWARD }, + + { 0x02, KEY_PREVIOUS }, + { 0x07, KEY_STOP }, + { 0x06, KEY_NEXT }, + + { 0x0c, KEY_UP }, + { 0x0e, KEY_DOWN }, + { 0x0b, KEY_LEFT }, + { 0x0d, KEY_RIGHT }, + { 0x11, KEY_OK }, + + { 0x03, KEY_MENU }, + { 0x09, KEY_SETUP }, + { 0x05, KEY_VIDEO }, + { 0x22, KEY_CHANNEL }, + + { 0x12, KEY_VOLUMEUP }, + { 0x15, KEY_VOLUMEDOWN }, + { 0x10, KEY_CHANNELUP }, + { 0x13, KEY_CHANNELDOWN }, + + { 0x04, KEY_RECORD }, + + { 0x16, KEY_1 }, + { 0x17, KEY_2 }, + { 0x18, KEY_3 }, + { 0x19, KEY_4 }, + { 0x1a, KEY_5 }, + { 0x1b, KEY_6 }, + { 0x1c, KEY_7 }, + { 0x1d, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x1f, KEY_0 }, + + { 0x20, KEY_LANGUAGE }, + { 0x21, KEY_SLEEP }, +}; + +struct ir_scancode_table ir_codes_videomate_tv_pvr_table = { + .scan = ir_codes_videomate_tv_pvr, + .size = ARRAY_SIZE(ir_codes_videomate_tv_pvr), +}; +EXPORT_SYMBOL_GPL(ir_codes_videomate_tv_pvr_table); /* Michael Tokarev <mjt@tls.msk.ru> http://www.corpit.ru/mjt/beholdTV/remote_control.jpg - keytable is used by MANLI MTV00[ 0x0c ] and BeholdTV 40[13] at + keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at least, and probably other cards too. The "ascii-art picture" below (in comments, first row is the keycode in hex, and subsequent row(s) shows the button labels (several variants when appropriate) helps to descide which keycodes to assign to the buttons. */ -IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_manli[] = { /* 0x1c 0x12 * * FUNCTION POWER * * FM (|) * * */ - [ 0x1c ] = KEY_RADIO, /*XXX*/ - [ 0x12 ] = KEY_POWER, + { 0x1c, KEY_RADIO }, /*XXX*/ + { 0x12, KEY_POWER }, /* 0x01 0x02 0x03 * * 1 2 3 * @@ -1248,29 +1339,29 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = { * 0x07 0x08 0x09 * * 7 8 9 * * */ - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, /* 0x0a 0x00 0x17 * * RECALL 0 +100 * * PLUS * * */ - [ 0x0a ] = KEY_AGAIN, /*XXX KEY_REWIND? */ - [ 0x00 ] = KEY_0, - [ 0x17 ] = KEY_DIGITS, /*XXX*/ + { 0x0a, KEY_AGAIN }, /*XXX KEY_REWIND? */ + { 0x00, KEY_0 }, + { 0x17, KEY_DIGITS }, /*XXX*/ /* 0x14 0x10 * * MENU INFO * * OSD */ - [ 0x14 ] = KEY_MENU, - [ 0x10 ] = KEY_INFO, + { 0x14, KEY_MENU }, + { 0x10, KEY_INFO }, /* 0x0b * * Up * @@ -1281,18 +1372,18 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = { * 0x015 * * Down * * */ - [ 0x0b ] = KEY_UP, /*XXX KEY_SCROLLUP? */ - [ 0x18 ] = KEY_LEFT, /*XXX KEY_BACK? */ - [ 0x16 ] = KEY_OK, /*XXX KEY_SELECT? KEY_ENTER? */ - [ 0x0c ] = KEY_RIGHT, /*XXX KEY_FORWARD? */ - [ 0x15 ] = KEY_DOWN, /*XXX KEY_SCROLLDOWN? */ + { 0x0b, KEY_UP }, + { 0x18, KEY_LEFT }, + { 0x16, KEY_OK }, /*XXX KEY_SELECT? KEY_ENTER? */ + { 0x0c, KEY_RIGHT }, + { 0x15, KEY_DOWN }, /* 0x11 0x0d * * TV/AV MODE * * SOURCE STEREO * * */ - [ 0x11 ] = KEY_TV, /*XXX*/ - [ 0x0d ] = KEY_MODE, /*XXX there's no KEY_STEREO */ + { 0x11, KEY_TV }, /*XXX*/ + { 0x0d, KEY_MODE }, /*XXX there's no KEY_STEREO */ /* 0x0f 0x1b 0x1a * * AUDIO Vol+ Chan+ * @@ -1301,891 +1392,967 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = { * 0x0e 0x1f 0x1e * * SLEEP Vol- Chan- * * */ - [ 0x0f ] = KEY_AUDIO, - [ 0x1b ] = KEY_VOLUMEUP, - [ 0x1a ] = KEY_CHANNELUP, - [ 0x0e ] = KEY_SLEEP, /*XXX maybe KEY_PAUSE */ - [ 0x1f ] = KEY_VOLUMEDOWN, - [ 0x1e ] = KEY_CHANNELDOWN, + { 0x0f, KEY_AUDIO }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1a, KEY_CHANNELUP }, + { 0x0e, KEY_TIME }, + { 0x1f, KEY_VOLUMEDOWN }, + { 0x1e, KEY_CHANNELDOWN }, /* 0x13 0x19 * * MUTE SNAPSHOT* * */ - [ 0x13 ] = KEY_MUTE, - [ 0x19 ] = KEY_RECORD, /*XXX*/ + { 0x13, KEY_MUTE }, + { 0x19, KEY_CAMERA }, - // 0x1d unused ? + /* 0x1d unused ? */ }; -EXPORT_SYMBOL_GPL(ir_codes_manli); +struct ir_scancode_table ir_codes_manli_table = { + .scan = ir_codes_manli, + .size = ARRAY_SIZE(ir_codes_manli), +}; +EXPORT_SYMBOL_GPL(ir_codes_manli_table); /* Mike Baikov <mike@baikov.com> */ -IR_KEYTAB_TYPE ir_codes_gotview7135[IR_KEYTAB_SIZE] = { - - [ 0x11 ] = KEY_POWER, - [ 0x35 ] = KEY_TV, - [ 0x1b ] = KEY_0, - [ 0x29 ] = KEY_1, - [ 0x19 ] = KEY_2, - [ 0x39 ] = KEY_3, - [ 0x1f ] = KEY_4, - [ 0x2c ] = KEY_5, - [ 0x21 ] = KEY_6, - [ 0x24 ] = KEY_7, - [ 0x18 ] = KEY_8, - [ 0x2b ] = KEY_9, - [ 0x3b ] = KEY_AGAIN, /* LOOP */ - [ 0x06 ] = KEY_AUDIO, - [ 0x31 ] = KEY_PRINT, /* PREVIEW */ - [ 0x3e ] = KEY_VIDEO, - [ 0x10 ] = KEY_CHANNELUP, - [ 0x20 ] = KEY_CHANNELDOWN, - [ 0x0c ] = KEY_VOLUMEDOWN, - [ 0x28 ] = KEY_VOLUMEUP, - [ 0x08 ] = KEY_MUTE, - [ 0x26 ] = KEY_SEARCH, /*SCAN*/ - [ 0x3f ] = KEY_SHUFFLE, /* SNAPSHOT */ - [ 0x12 ] = KEY_RECORD, - [ 0x32 ] = KEY_STOP, - [ 0x3c ] = KEY_PLAY, - [ 0x1d ] = KEY_REWIND, - [ 0x2d ] = KEY_PAUSE, - [ 0x0d ] = KEY_FORWARD, - [ 0x05 ] = KEY_ZOOM, /*FULL*/ - - [ 0x2a ] = KEY_F21, /* LIVE TIMESHIFT */ - [ 0x0e ] = KEY_F22, /* MIN TIMESHIFT */ - [ 0x1e ] = KEY_F23, /* TIMESHIFT */ - [ 0x38 ] = KEY_F24, /* NORMAL TIMESHIFT */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_gotview7135); - -IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE] = { - [ 0x03 ] = KEY_POWER, - [ 0x6f ] = KEY_MUTE, - [ 0x10 ] = KEY_BACKSPACE, /* Recall */ - - [ 0x11 ] = KEY_0, - [ 0x04 ] = KEY_1, - [ 0x05 ] = KEY_2, - [ 0x06 ] = KEY_3, - [ 0x08 ] = KEY_4, - [ 0x09 ] = KEY_5, - [ 0x0a ] = KEY_6, - [ 0x0c ] = KEY_7, - [ 0x0d ] = KEY_8, - [ 0x0e ] = KEY_9, - [ 0x12 ] = KEY_DOT, /* 100+ */ - - [ 0x07 ] = KEY_VOLUMEUP, - [ 0x0b ] = KEY_VOLUMEDOWN, - [ 0x1a ] = KEY_KPPLUS, - [ 0x18 ] = KEY_KPMINUS, - [ 0x15 ] = KEY_UP, - [ 0x1d ] = KEY_DOWN, - [ 0x0f ] = KEY_CHANNELUP, - [ 0x13 ] = KEY_CHANNELDOWN, - [ 0x48 ] = KEY_ZOOM, - - [ 0x1b ] = KEY_VIDEO, /* Video source */ - [ 0x49 ] = KEY_LANGUAGE, /* MTS Select */ - [ 0x19 ] = KEY_SEARCH, /* Auto Scan */ - - [ 0x4b ] = KEY_RECORD, - [ 0x46 ] = KEY_PLAY, - [ 0x45 ] = KEY_PAUSE, /* Pause */ - [ 0x44 ] = KEY_STOP, - [ 0x40 ] = KEY_FORWARD, /* Forward ? */ - [ 0x42 ] = KEY_REWIND, /* Backward ? */ - -}; - -EXPORT_SYMBOL_GPL(ir_codes_purpletv); +static struct ir_scancode ir_codes_gotview7135[] = { + + { 0x11, KEY_POWER }, + { 0x35, KEY_TV }, + { 0x1b, KEY_0 }, + { 0x29, KEY_1 }, + { 0x19, KEY_2 }, + { 0x39, KEY_3 }, + { 0x1f, KEY_4 }, + { 0x2c, KEY_5 }, + { 0x21, KEY_6 }, + { 0x24, KEY_7 }, + { 0x18, KEY_8 }, + { 0x2b, KEY_9 }, + { 0x3b, KEY_AGAIN }, /* LOOP */ + { 0x06, KEY_AUDIO }, + { 0x31, KEY_PRINT }, /* PREVIEW */ + { 0x3e, KEY_VIDEO }, + { 0x10, KEY_CHANNELUP }, + { 0x20, KEY_CHANNELDOWN }, + { 0x0c, KEY_VOLUMEDOWN }, + { 0x28, KEY_VOLUMEUP }, + { 0x08, KEY_MUTE }, + { 0x26, KEY_SEARCH }, /* SCAN */ + { 0x3f, KEY_CAMERA }, /* SNAPSHOT */ + { 0x12, KEY_RECORD }, + { 0x32, KEY_STOP }, + { 0x3c, KEY_PLAY }, + { 0x1d, KEY_REWIND }, + { 0x2d, KEY_PAUSE }, + { 0x0d, KEY_FORWARD }, + { 0x05, KEY_ZOOM }, /*FULL*/ + + { 0x2a, KEY_F21 }, /* LIVE TIMESHIFT */ + { 0x0e, KEY_F22 }, /* MIN TIMESHIFT */ + { 0x1e, KEY_TIME }, /* TIMESHIFT */ + { 0x38, KEY_F24 }, /* NORMAL TIMESHIFT */ +}; + +struct ir_scancode_table ir_codes_gotview7135_table = { + .scan = ir_codes_gotview7135, + .size = ARRAY_SIZE(ir_codes_gotview7135), +}; +EXPORT_SYMBOL_GPL(ir_codes_gotview7135_table); + +static struct ir_scancode ir_codes_purpletv[] = { + { 0x03, KEY_POWER }, + { 0x6f, KEY_MUTE }, + { 0x10, KEY_BACKSPACE }, /* Recall */ + + { 0x11, KEY_0 }, + { 0x04, KEY_1 }, + { 0x05, KEY_2 }, + { 0x06, KEY_3 }, + { 0x08, KEY_4 }, + { 0x09, KEY_5 }, + { 0x0a, KEY_6 }, + { 0x0c, KEY_7 }, + { 0x0d, KEY_8 }, + { 0x0e, KEY_9 }, + { 0x12, KEY_DOT }, /* 100+ */ + + { 0x07, KEY_VOLUMEUP }, + { 0x0b, KEY_VOLUMEDOWN }, + { 0x1a, KEY_KPPLUS }, + { 0x18, KEY_KPMINUS }, + { 0x15, KEY_UP }, + { 0x1d, KEY_DOWN }, + { 0x0f, KEY_CHANNELUP }, + { 0x13, KEY_CHANNELDOWN }, + { 0x48, KEY_ZOOM }, + + { 0x1b, KEY_VIDEO }, /* Video source */ + { 0x1f, KEY_CAMERA }, /* Snapshot */ + { 0x49, KEY_LANGUAGE }, /* MTS Select */ + { 0x19, KEY_SEARCH }, /* Auto Scan */ + + { 0x4b, KEY_RECORD }, + { 0x46, KEY_PLAY }, + { 0x45, KEY_PAUSE }, /* Pause */ + { 0x44, KEY_STOP }, + { 0x43, KEY_TIME }, /* Time Shift */ + { 0x17, KEY_CHANNEL }, /* SURF CH */ + { 0x40, KEY_FORWARD }, /* Forward ? */ + { 0x42, KEY_REWIND }, /* Backward ? */ + +}; + +struct ir_scancode_table ir_codes_purpletv_table = { + .scan = ir_codes_purpletv, + .size = ARRAY_SIZE(ir_codes_purpletv), +}; +EXPORT_SYMBOL_GPL(ir_codes_purpletv_table); /* Mapping for the 28 key remote control as seen at http://www.sednacomputer.com/photo/cardbus-tv.jpg Pavel Mihaylov <bin@bash.info> Also for the remote bundled with Kozumi KTV-01C card */ -IR_KEYTAB_TYPE ir_codes_pctv_sedna[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0a ] = KEY_AGAIN, /* Recall */ - [ 0x0b ] = KEY_CHANNELUP, - [ 0x0c ] = KEY_VOLUMEUP, - [ 0x0d ] = KEY_MODE, /* Stereo */ - [ 0x0e ] = KEY_STOP, - [ 0x0f ] = KEY_PREVIOUSSONG, - [ 0x10 ] = KEY_ZOOM, - [ 0x11 ] = KEY_TUNER, /* Source */ - [ 0x12 ] = KEY_POWER, - [ 0x13 ] = KEY_MUTE, - [ 0x15 ] = KEY_CHANNELDOWN, - [ 0x18 ] = KEY_VOLUMEDOWN, - [ 0x19 ] = KEY_SHUFFLE, /* Snapshot */ - [ 0x1a ] = KEY_NEXTSONG, - [ 0x1b ] = KEY_TEXT, /* Time Shift */ - [ 0x1c ] = KEY_RADIO, /* FM Radio */ - [ 0x1d ] = KEY_RECORD, - [ 0x1e ] = KEY_PAUSE, +static struct ir_scancode ir_codes_pctv_sedna[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_AGAIN }, /* Recall */ + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_VOLUMEUP }, + { 0x0d, KEY_MODE }, /* Stereo */ + { 0x0e, KEY_STOP }, + { 0x0f, KEY_PREVIOUSSONG }, + { 0x10, KEY_ZOOM }, + { 0x11, KEY_TUNER }, /* Source */ + { 0x12, KEY_POWER }, + { 0x13, KEY_MUTE }, + { 0x15, KEY_CHANNELDOWN }, + { 0x18, KEY_VOLUMEDOWN }, + { 0x19, KEY_CAMERA }, /* Snapshot */ + { 0x1a, KEY_NEXTSONG }, + { 0x1b, KEY_TIME }, /* Time Shift */ + { 0x1c, KEY_RADIO }, /* FM Radio */ + { 0x1d, KEY_RECORD }, + { 0x1e, KEY_PAUSE }, /* additional codes for Kozumi's remote */ - [0x14] = KEY_INFO, /* OSD */ - [0x16] = KEY_OK, /* OK */ - [0x17] = KEY_DIGITS, /* Plus */ - [0x1f] = KEY_PLAY, /* Play */ + { 0x14, KEY_INFO }, /* OSD */ + { 0x16, KEY_OK }, /* OK */ + { 0x17, KEY_DIGITS }, /* Plus */ + { 0x1f, KEY_PLAY }, /* Play */ }; -EXPORT_SYMBOL_GPL(ir_codes_pctv_sedna); +struct ir_scancode_table ir_codes_pctv_sedna_table = { + .scan = ir_codes_pctv_sedna, + .size = ARRAY_SIZE(ir_codes_pctv_sedna), +}; +EXPORT_SYMBOL_GPL(ir_codes_pctv_sedna_table); /* Mark Phalan <phalanm@o2.ie> */ -IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x12 ] = KEY_POWER, - [ 0x10 ] = KEY_MUTE, - [ 0x1f ] = KEY_VOLUMEDOWN, - [ 0x1b ] = KEY_VOLUMEUP, - [ 0x1a ] = KEY_CHANNELUP, - [ 0x1e ] = KEY_CHANNELDOWN, - [ 0x0e ] = KEY_PAGEUP, - [ 0x1d ] = KEY_PAGEDOWN, - [ 0x13 ] = KEY_SOUND, - - [ 0x18 ] = KEY_KPPLUSMINUS, /* CH +/- */ - [ 0x16 ] = KEY_SUBTITLE, /* CC */ - [ 0x0d ] = KEY_TEXT, /* TTX */ - [ 0x0b ] = KEY_TV, /* AIR/CBL */ - [ 0x11 ] = KEY_PC, /* PC/TV */ - [ 0x17 ] = KEY_OK, /* CH RTN */ - [ 0x19 ] = KEY_MODE, /* FUNC */ - [ 0x0c ] = KEY_SEARCH, /* AUTOSCAN */ +static struct ir_scancode ir_codes_pv951[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x12, KEY_POWER }, + { 0x10, KEY_MUTE }, + { 0x1f, KEY_VOLUMEDOWN }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1a, KEY_CHANNELUP }, + { 0x1e, KEY_CHANNELDOWN }, + { 0x0e, KEY_PAGEUP }, + { 0x1d, KEY_PAGEDOWN }, + { 0x13, KEY_SOUND }, + + { 0x18, KEY_KPPLUSMINUS }, /* CH +/- */ + { 0x16, KEY_SUBTITLE }, /* CC */ + { 0x0d, KEY_TEXT }, /* TTX */ + { 0x0b, KEY_TV }, /* AIR/CBL */ + { 0x11, KEY_PC }, /* PC/TV */ + { 0x17, KEY_OK }, /* CH RTN */ + { 0x19, KEY_MODE }, /* FUNC */ + { 0x0c, KEY_SEARCH }, /* AUTOSCAN */ /* Not sure what to do with these ones! */ - [ 0x0f ] = KEY_SELECT, /* SOURCE */ - [ 0x0a ] = KEY_KPPLUS, /* +100 */ - [ 0x14 ] = KEY_EQUAL, /* SYNC */ - [ 0x1c ] = KEY_MEDIA, /* PC/TV */ + { 0x0f, KEY_SELECT }, /* SOURCE */ + { 0x0a, KEY_KPPLUS }, /* +100 */ + { 0x14, KEY_EQUAL }, /* SYNC */ + { 0x1c, KEY_MEDIA }, /* PC/TV */ }; -EXPORT_SYMBOL_GPL(ir_codes_pv951); +struct ir_scancode_table ir_codes_pv951_table = { + .scan = ir_codes_pv951, + .size = ARRAY_SIZE(ir_codes_pv951), +}; +EXPORT_SYMBOL_GPL(ir_codes_pv951_table); /* generic RC5 keytable */ /* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */ /* used by old (black) Hauppauge remotes */ -IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_rc5_tv[] = { /* Keys 0 to 9 */ - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0b ] = KEY_CHANNEL, /* channel / program (japan: 11) */ - [ 0x0c ] = KEY_POWER, /* standby */ - [ 0x0d ] = KEY_MUTE, /* mute / demute */ - [ 0x0f ] = KEY_TV, /* display */ - [ 0x10 ] = KEY_VOLUMEUP, - [ 0x11 ] = KEY_VOLUMEDOWN, - [ 0x12 ] = KEY_BRIGHTNESSUP, - [ 0x13 ] = KEY_BRIGHTNESSDOWN, - [ 0x1e ] = KEY_SEARCH, /* search + */ - [ 0x20 ] = KEY_CHANNELUP, /* channel / program + */ - [ 0x21 ] = KEY_CHANNELDOWN, /* channel / program - */ - [ 0x22 ] = KEY_CHANNEL, /* alt / channel */ - [ 0x23 ] = KEY_LANGUAGE, /* 1st / 2nd language */ - [ 0x26 ] = KEY_SLEEP, /* sleeptimer */ - [ 0x2e ] = KEY_MENU, /* 2nd controls (USA: menu) */ - [ 0x30 ] = KEY_PAUSE, - [ 0x32 ] = KEY_REWIND, - [ 0x33 ] = KEY_GOTO, - [ 0x35 ] = KEY_PLAY, - [ 0x36 ] = KEY_STOP, - [ 0x37 ] = KEY_RECORD, /* recording */ - [ 0x3c ] = KEY_TEXT, /* teletext submode (Japan: 12) */ - [ 0x3d ] = KEY_SUSPEND, /* system standby */ - -}; - -EXPORT_SYMBOL_GPL(ir_codes_rc5_tv); + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0b, KEY_CHANNEL }, /* channel / program (japan: 11) */ + { 0x0c, KEY_POWER }, /* standby */ + { 0x0d, KEY_MUTE }, /* mute / demute */ + { 0x0f, KEY_TV }, /* display */ + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x12, KEY_BRIGHTNESSUP }, + { 0x13, KEY_BRIGHTNESSDOWN }, + { 0x1e, KEY_SEARCH }, /* search + */ + { 0x20, KEY_CHANNELUP }, /* channel / program + */ + { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x22, KEY_CHANNEL }, /* alt / channel */ + { 0x23, KEY_LANGUAGE }, /* 1st / 2nd language */ + { 0x26, KEY_SLEEP }, /* sleeptimer */ + { 0x2e, KEY_MENU }, /* 2nd controls (USA: menu) */ + { 0x30, KEY_PAUSE }, + { 0x32, KEY_REWIND }, + { 0x33, KEY_GOTO }, + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, /* recording */ + { 0x3c, KEY_TEXT }, /* teletext submode (Japan: 12) */ + { 0x3d, KEY_SUSPEND }, /* system standby */ + +}; + +struct ir_scancode_table ir_codes_rc5_tv_table = { + .scan = ir_codes_rc5_tv, + .size = ARRAY_SIZE(ir_codes_rc5_tv), +}; +EXPORT_SYMBOL_GPL(ir_codes_rc5_tv_table); /* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */ -IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_winfast[] = { /* Keys 0 to 9 */ - [ 0x12 ] = KEY_0, - [ 0x05 ] = KEY_1, - [ 0x06 ] = KEY_2, - [ 0x07 ] = KEY_3, - [ 0x09 ] = KEY_4, - [ 0x0a ] = KEY_5, - [ 0x0b ] = KEY_6, - [ 0x0d ] = KEY_7, - [ 0x0e ] = KEY_8, - [ 0x0f ] = KEY_9, - - [ 0x00 ] = KEY_POWER, - [ 0x1b ] = KEY_AUDIO, /* Audio Source */ - [ 0x02 ] = KEY_TUNER, /* TV/FM, not on Y0400052 */ - [ 0x1e ] = KEY_VIDEO, /* Video Source */ - [ 0x16 ] = KEY_INFO, /* Display information */ - [ 0x04 ] = KEY_VOLUMEUP, - [ 0x08 ] = KEY_VOLUMEDOWN, - [ 0x0c ] = KEY_CHANNELUP, - [ 0x10 ] = KEY_CHANNELDOWN, - [ 0x03 ] = KEY_ZOOM, /* fullscreen */ - [ 0x1f ] = KEY_TEXT, /* closed caption/teletext */ - [ 0x20 ] = KEY_SLEEP, - [ 0x29 ] = KEY_CLEAR, /* boss key */ - [ 0x14 ] = KEY_MUTE, - [ 0x2b ] = KEY_RED, - [ 0x2c ] = KEY_GREEN, - [ 0x2d ] = KEY_YELLOW, - [ 0x2e ] = KEY_BLUE, - [ 0x18 ] = KEY_KPPLUS, /* fine tune + , not on Y040052 */ - [ 0x19 ] = KEY_KPMINUS, /* fine tune - , not on Y040052 */ - [ 0x2a ] = KEY_MEDIA, /* PIP (Picture in picture */ - [ 0x21 ] = KEY_DOT, - [ 0x13 ] = KEY_ENTER, - [ 0x11 ] = KEY_LAST, /* Recall (last channel */ - [ 0x22 ] = KEY_PREVIOUS, - [ 0x23 ] = KEY_PLAYPAUSE, - [ 0x24 ] = KEY_NEXT, - [ 0x25 ] = KEY_ARCHIVE, /* Time Shifting */ - [ 0x26 ] = KEY_STOP, - [ 0x27 ] = KEY_RECORD, - [ 0x28 ] = KEY_SAVE, /* Screenshot */ - [ 0x2f ] = KEY_MENU, - [ 0x30 ] = KEY_CANCEL, - [ 0x31 ] = KEY_CHANNEL, /* Channel Surf */ - [ 0x32 ] = KEY_SUBTITLE, - [ 0x33 ] = KEY_LANGUAGE, - [ 0x34 ] = KEY_REWIND, - [ 0x35 ] = KEY_FASTFORWARD, - [ 0x36 ] = KEY_TV, - [ 0x37 ] = KEY_RADIO, /* FM */ - [ 0x38 ] = KEY_DVD, - - [ 0x3e ] = KEY_F21, /* MCE +VOL, on Y04G0033 */ - [ 0x3a ] = KEY_F22, /* MCE -VOL, on Y04G0033 */ - [ 0x3b ] = KEY_F23, /* MCE +CH, on Y04G0033 */ - [ 0x3f ] = KEY_F24 /* MCE -CH, on Y04G0033 */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_winfast); - -IR_KEYTAB_TYPE ir_codes_pinnacle_color[IR_KEYTAB_SIZE] = { - [ 0x59 ] = KEY_MUTE, - [ 0x4a ] = KEY_POWER, - - [ 0x18 ] = KEY_TEXT, - [ 0x26 ] = KEY_TV, - [ 0x3d ] = KEY_PRINT, - - [ 0x48 ] = KEY_RED, - [ 0x04 ] = KEY_GREEN, - [ 0x11 ] = KEY_YELLOW, - [ 0x00 ] = KEY_BLUE, - - [ 0x2d ] = KEY_VOLUMEUP, - [ 0x1e ] = KEY_VOLUMEDOWN, - - [ 0x49 ] = KEY_MENU, - - [ 0x16 ] = KEY_CHANNELUP, - [ 0x17 ] = KEY_CHANNELDOWN, - - [ 0x20 ] = KEY_UP, - [ 0x21 ] = KEY_DOWN, - [ 0x22 ] = KEY_LEFT, - [ 0x23 ] = KEY_RIGHT, - [ 0x0d ] = KEY_SELECT, - - - - [ 0x08 ] = KEY_BACK, - [ 0x07 ] = KEY_REFRESH, - - [ 0x2f ] = KEY_ZOOM, - [ 0x29 ] = KEY_RECORD, - - [ 0x4b ] = KEY_PAUSE, - [ 0x4d ] = KEY_REWIND, - [ 0x2e ] = KEY_PLAY, - [ 0x4e ] = KEY_FORWARD, - [ 0x53 ] = KEY_PREVIOUS, - [ 0x4c ] = KEY_STOP, - [ 0x54 ] = KEY_NEXT, - - [ 0x69 ] = KEY_0, - [ 0x6a ] = KEY_1, - [ 0x6b ] = KEY_2, - [ 0x6c ] = KEY_3, - [ 0x6d ] = KEY_4, - [ 0x6e ] = KEY_5, - [ 0x6f ] = KEY_6, - [ 0x70 ] = KEY_7, - [ 0x71 ] = KEY_8, - [ 0x72 ] = KEY_9, - - [ 0x74 ] = KEY_CHANNEL, - [ 0x0a ] = KEY_BACKSPACE, -}; - -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_color); + { 0x12, KEY_0 }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + + { 0x00, KEY_POWER }, + { 0x1b, KEY_AUDIO }, /* Audio Source */ + { 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */ + { 0x1e, KEY_VIDEO }, /* Video Source */ + { 0x16, KEY_INFO }, /* Display information */ + { 0x04, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x0c, KEY_CHANNELUP }, + { 0x10, KEY_CHANNELDOWN }, + { 0x03, KEY_ZOOM }, /* fullscreen */ + { 0x1f, KEY_TEXT }, /* closed caption/teletext */ + { 0x20, KEY_SLEEP }, + { 0x29, KEY_CLEAR }, /* boss key */ + { 0x14, KEY_MUTE }, + { 0x2b, KEY_RED }, + { 0x2c, KEY_GREEN }, + { 0x2d, KEY_YELLOW }, + { 0x2e, KEY_BLUE }, + { 0x18, KEY_KPPLUS }, /* fine tune + , not on Y040052 */ + { 0x19, KEY_KPMINUS }, /* fine tune - , not on Y040052 */ + { 0x2a, KEY_MEDIA }, /* PIP (Picture in picture */ + { 0x21, KEY_DOT }, + { 0x13, KEY_ENTER }, + { 0x11, KEY_LAST }, /* Recall (last channel */ + { 0x22, KEY_PREVIOUS }, + { 0x23, KEY_PLAYPAUSE }, + { 0x24, KEY_NEXT }, + { 0x25, KEY_TIME }, /* Time Shifting */ + { 0x26, KEY_STOP }, + { 0x27, KEY_RECORD }, + { 0x28, KEY_SAVE }, /* Screenshot */ + { 0x2f, KEY_MENU }, + { 0x30, KEY_CANCEL }, + { 0x31, KEY_CHANNEL }, /* Channel Surf */ + { 0x32, KEY_SUBTITLE }, + { 0x33, KEY_LANGUAGE }, + { 0x34, KEY_REWIND }, + { 0x35, KEY_FASTFORWARD }, + { 0x36, KEY_TV }, + { 0x37, KEY_RADIO }, /* FM */ + { 0x38, KEY_DVD }, + + { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ + { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ + { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ + { 0x3f, KEY_F24 } /* MCE -CH, on Y04G0033 */ +}; + +struct ir_scancode_table ir_codes_winfast_table = { + .scan = ir_codes_winfast, + .size = ARRAY_SIZE(ir_codes_winfast), +}; +EXPORT_SYMBOL_GPL(ir_codes_winfast_table); + +static struct ir_scancode ir_codes_pinnacle_color[] = { + { 0x59, KEY_MUTE }, + { 0x4a, KEY_POWER }, + + { 0x18, KEY_TEXT }, + { 0x26, KEY_TV }, + { 0x3d, KEY_PRINT }, + + { 0x48, KEY_RED }, + { 0x04, KEY_GREEN }, + { 0x11, KEY_YELLOW }, + { 0x00, KEY_BLUE }, + + { 0x2d, KEY_VOLUMEUP }, + { 0x1e, KEY_VOLUMEDOWN }, + + { 0x49, KEY_MENU }, + + { 0x16, KEY_CHANNELUP }, + { 0x17, KEY_CHANNELDOWN }, + + { 0x20, KEY_UP }, + { 0x21, KEY_DOWN }, + { 0x22, KEY_LEFT }, + { 0x23, KEY_RIGHT }, + { 0x0d, KEY_SELECT }, + + { 0x08, KEY_BACK }, + { 0x07, KEY_REFRESH }, + + { 0x2f, KEY_ZOOM }, + { 0x29, KEY_RECORD }, + + { 0x4b, KEY_PAUSE }, + { 0x4d, KEY_REWIND }, + { 0x2e, KEY_PLAY }, + { 0x4e, KEY_FORWARD }, + { 0x53, KEY_PREVIOUS }, + { 0x4c, KEY_STOP }, + { 0x54, KEY_NEXT }, + + { 0x69, KEY_0 }, + { 0x6a, KEY_1 }, + { 0x6b, KEY_2 }, + { 0x6c, KEY_3 }, + { 0x6d, KEY_4 }, + { 0x6e, KEY_5 }, + { 0x6f, KEY_6 }, + { 0x70, KEY_7 }, + { 0x71, KEY_8 }, + { 0x72, KEY_9 }, + + { 0x74, KEY_CHANNEL }, + { 0x0a, KEY_BACKSPACE }, +}; + +struct ir_scancode_table ir_codes_pinnacle_color_table = { + .scan = ir_codes_pinnacle_color, + .size = ARRAY_SIZE(ir_codes_pinnacle_color), +}; +EXPORT_SYMBOL_GPL(ir_codes_pinnacle_color_table); /* Hauppauge: the newer, gray remotes (seems there are multiple * slightly different versions), shipped with cx88+ivtv cards. * almost rc5 coding, but some non-standard keys */ -IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_hauppauge_new[] = { /* Keys 0 to 9 */ - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - - [ 0x0a ] = KEY_TEXT, /* keypad asterisk as well */ - [ 0x0b ] = KEY_RED, /* red button */ - [ 0x0c ] = KEY_RADIO, - [ 0x0d ] = KEY_MENU, - [ 0x0e ] = KEY_SUBTITLE, /* also the # key */ - [ 0x0f ] = KEY_MUTE, - [ 0x10 ] = KEY_VOLUMEUP, - [ 0x11 ] = KEY_VOLUMEDOWN, - [ 0x12 ] = KEY_PREVIOUS, /* previous channel */ - [ 0x14 ] = KEY_UP, - [ 0x15 ] = KEY_DOWN, - [ 0x16 ] = KEY_LEFT, - [ 0x17 ] = KEY_RIGHT, - [ 0x18 ] = KEY_VIDEO, /* Videos */ - [ 0x19 ] = KEY_AUDIO, /* Music */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x0b, KEY_RED }, /* red button */ + { 0x0c, KEY_RADIO }, + { 0x0d, KEY_MENU }, + { 0x0e, KEY_SUBTITLE }, /* also the # key */ + { 0x0f, KEY_MUTE }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x12, KEY_PREVIOUS }, /* previous channel */ + { 0x14, KEY_UP }, + { 0x15, KEY_DOWN }, + { 0x16, KEY_LEFT }, + { 0x17, KEY_RIGHT }, + { 0x18, KEY_VIDEO }, /* Videos */ + { 0x19, KEY_AUDIO }, /* Music */ /* 0x1a: Pictures - presume this means "Multimedia Home Platform" - no "PICTURES" key in input.h */ - [ 0x1a ] = KEY_MHP, - - [ 0x1b ] = KEY_EPG, /* Guide */ - [ 0x1c ] = KEY_TV, - [ 0x1e ] = KEY_NEXTSONG, /* skip >| */ - [ 0x1f ] = KEY_EXIT, /* back/exit */ - [ 0x20 ] = KEY_CHANNELUP, /* channel / program + */ - [ 0x21 ] = KEY_CHANNELDOWN, /* channel / program - */ - [ 0x22 ] = KEY_CHANNEL, /* source (old black remote) */ - [ 0x24 ] = KEY_PREVIOUSSONG, /* replay |< */ - [ 0x25 ] = KEY_ENTER, /* OK */ - [ 0x26 ] = KEY_SLEEP, /* minimize (old black remote) */ - [ 0x29 ] = KEY_BLUE, /* blue key */ - [ 0x2e ] = KEY_GREEN, /* green button */ - [ 0x30 ] = KEY_PAUSE, /* pause */ - [ 0x32 ] = KEY_REWIND, /* backward << */ - [ 0x34 ] = KEY_FASTFORWARD, /* forward >> */ - [ 0x35 ] = KEY_PLAY, - [ 0x36 ] = KEY_STOP, - [ 0x37 ] = KEY_RECORD, /* recording */ - [ 0x38 ] = KEY_YELLOW, /* yellow key */ - [ 0x3b ] = KEY_SELECT, /* top right button */ - [ 0x3c ] = KEY_ZOOM, /* full */ - [ 0x3d ] = KEY_POWER, /* system power (green button) */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new); - -IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE] = { - [ 0x1d ] = KEY_SWITCHVIDEOMODE, /* switch inputs */ - [ 0x2a ] = KEY_FRONT, - - [ 0x3e ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x06 ] = KEY_3, - [ 0x0a ] = KEY_4, - [ 0x0e ] = KEY_5, - [ 0x12 ] = KEY_6, - [ 0x16 ] = KEY_7, - [ 0x1a ] = KEY_8, - [ 0x1e ] = KEY_9, - [ 0x3a ] = KEY_0, - [ 0x22 ] = KEY_NUMLOCK, /* -/-- */ - [ 0x20 ] = KEY_REFRESH, - - [ 0x03 ] = KEY_BRIGHTNESSDOWN, - [ 0x28 ] = KEY_AUDIO, - [ 0x3c ] = KEY_UP, - [ 0x3f ] = KEY_LEFT, - [ 0x2e ] = KEY_MUTE, - [ 0x3b ] = KEY_RIGHT, - [ 0x00 ] = KEY_DOWN, - [ 0x07 ] = KEY_BRIGHTNESSUP, - [ 0x2c ] = KEY_TEXT, - - [ 0x37 ] = KEY_RECORD, - [ 0x17 ] = KEY_PLAY, - [ 0x13 ] = KEY_PAUSE, - [ 0x26 ] = KEY_STOP, - [ 0x18 ] = KEY_FASTFORWARD, - [ 0x14 ] = KEY_REWIND, - [ 0x33 ] = KEY_ZOOM, - [ 0x32 ] = KEY_KEYBOARD, - [ 0x30 ] = KEY_GOTO, /* Pointing arrow */ - [ 0x36 ] = KEY_MACRO, /* Maximize/Minimize (yellow) */ - [ 0x0b ] = KEY_RADIO, - [ 0x10 ] = KEY_POWER, - -}; - -EXPORT_SYMBOL_GPL(ir_codes_npgtech); + { 0x1a, KEY_MHP }, + + { 0x1b, KEY_EPG }, /* Guide */ + { 0x1c, KEY_TV }, + { 0x1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1f, KEY_EXIT }, /* back/exit */ + { 0x20, KEY_CHANNELUP }, /* channel / program + */ + { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x25, KEY_ENTER }, /* OK */ + { 0x26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x29, KEY_BLUE }, /* blue key */ + { 0x2e, KEY_GREEN }, /* green button */ + { 0x30, KEY_PAUSE }, /* pause */ + { 0x32, KEY_REWIND }, /* backward << */ + { 0x34, KEY_FASTFORWARD }, /* forward >> */ + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, /* recording */ + { 0x38, KEY_YELLOW }, /* yellow key */ + { 0x3b, KEY_SELECT }, /* top right button */ + { 0x3c, KEY_ZOOM }, /* full */ + { 0x3d, KEY_POWER }, /* system power (green button) */ +}; + +struct ir_scancode_table ir_codes_hauppauge_new_table = { + .scan = ir_codes_hauppauge_new, + .size = ARRAY_SIZE(ir_codes_hauppauge_new), +}; +EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table); + +static struct ir_scancode ir_codes_npgtech[] = { + { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ + { 0x2a, KEY_FRONT }, + + { 0x3e, KEY_1 }, + { 0x02, KEY_2 }, + { 0x06, KEY_3 }, + { 0x0a, KEY_4 }, + { 0x0e, KEY_5 }, + { 0x12, KEY_6 }, + { 0x16, KEY_7 }, + { 0x1a, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x3a, KEY_0 }, + { 0x22, KEY_NUMLOCK }, /* -/-- */ + { 0x20, KEY_REFRESH }, + + { 0x03, KEY_BRIGHTNESSDOWN }, + { 0x28, KEY_AUDIO }, + { 0x3c, KEY_CHANNELUP }, + { 0x3f, KEY_VOLUMEDOWN }, + { 0x2e, KEY_MUTE }, + { 0x3b, KEY_VOLUMEUP }, + { 0x00, KEY_CHANNELDOWN }, + { 0x07, KEY_BRIGHTNESSUP }, + { 0x2c, KEY_TEXT }, + + { 0x37, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x13, KEY_PAUSE }, + { 0x26, KEY_STOP }, + { 0x18, KEY_FASTFORWARD }, + { 0x14, KEY_REWIND }, + { 0x33, KEY_ZOOM }, + { 0x32, KEY_KEYBOARD }, + { 0x30, KEY_GOTO }, /* Pointing arrow */ + { 0x36, KEY_MACRO }, /* Maximize/Minimize (yellow) */ + { 0x0b, KEY_RADIO }, + { 0x10, KEY_POWER }, + +}; + +struct ir_scancode_table ir_codes_npgtech_table = { + .scan = ir_codes_npgtech, + .size = ARRAY_SIZE(ir_codes_npgtech), +}; +EXPORT_SYMBOL_GPL(ir_codes_npgtech_table); /* Norwood Micro (non-Pro) TV Tuner By Peter Naulls <peter@chocky.org> Key comments are the functions given in the manual */ -IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_norwood[] = { /* Keys 0 to 9 */ - [ 0x20 ] = KEY_0, - [ 0x21 ] = KEY_1, - [ 0x22 ] = KEY_2, - [ 0x23 ] = KEY_3, - [ 0x24 ] = KEY_4, - [ 0x25 ] = KEY_5, - [ 0x26 ] = KEY_6, - [ 0x27 ] = KEY_7, - [ 0x28 ] = KEY_8, - [ 0x29 ] = KEY_9, - - [ 0x78 ] = KEY_TUNER, /* Video Source */ - [ 0x2c ] = KEY_EXIT, /* Open/Close software */ - [ 0x2a ] = KEY_SELECT, /* 2 Digit Select */ - [ 0x69 ] = KEY_AGAIN, /* Recall */ - - [ 0x32 ] = KEY_BRIGHTNESSUP, /* Brightness increase */ - [ 0x33 ] = KEY_BRIGHTNESSDOWN, /* Brightness decrease */ - [ 0x6b ] = KEY_KPPLUS, /* (not named >>>>>) */ - [ 0x6c ] = KEY_KPMINUS, /* (not named <<<<<) */ - - [ 0x2d ] = KEY_MUTE, /* Mute */ - [ 0x30 ] = KEY_VOLUMEUP, /* Volume up */ - [ 0x31 ] = KEY_VOLUMEDOWN, /* Volume down */ - [ 0x60 ] = KEY_CHANNELUP, /* Channel up */ - [ 0x61 ] = KEY_CHANNELDOWN, /* Channel down */ - - [ 0x3f ] = KEY_RECORD, /* Record */ - [ 0x37 ] = KEY_PLAY, /* Play */ - [ 0x36 ] = KEY_PAUSE, /* Pause */ - [ 0x2b ] = KEY_STOP, /* Stop */ - [ 0x67 ] = KEY_FASTFORWARD, /* Foward */ - [ 0x66 ] = KEY_REWIND, /* Rewind */ - [ 0x3e ] = KEY_SEARCH, /* Auto Scan */ - [ 0x2e ] = KEY_CAMERA, /* Capture Video */ - [ 0x6d ] = KEY_MENU, /* Show/Hide Control */ - [ 0x2f ] = KEY_ZOOM, /* Full Screen */ - [ 0x34 ] = KEY_RADIO, /* FM */ - [ 0x65 ] = KEY_POWER, /* Computer power */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_norwood); + { 0x20, KEY_0 }, + { 0x21, KEY_1 }, + { 0x22, KEY_2 }, + { 0x23, KEY_3 }, + { 0x24, KEY_4 }, + { 0x25, KEY_5 }, + { 0x26, KEY_6 }, + { 0x27, KEY_7 }, + { 0x28, KEY_8 }, + { 0x29, KEY_9 }, + + { 0x78, KEY_TUNER }, /* Video Source */ + { 0x2c, KEY_EXIT }, /* Open/Close software */ + { 0x2a, KEY_SELECT }, /* 2 Digit Select */ + { 0x69, KEY_AGAIN }, /* Recall */ + + { 0x32, KEY_BRIGHTNESSUP }, /* Brightness increase */ + { 0x33, KEY_BRIGHTNESSDOWN }, /* Brightness decrease */ + { 0x6b, KEY_KPPLUS }, /* (not named >>>>>) */ + { 0x6c, KEY_KPMINUS }, /* (not named <<<<<) */ + + { 0x2d, KEY_MUTE }, /* Mute */ + { 0x30, KEY_VOLUMEUP }, /* Volume up */ + { 0x31, KEY_VOLUMEDOWN }, /* Volume down */ + { 0x60, KEY_CHANNELUP }, /* Channel up */ + { 0x61, KEY_CHANNELDOWN }, /* Channel down */ + + { 0x3f, KEY_RECORD }, /* Record */ + { 0x37, KEY_PLAY }, /* Play */ + { 0x36, KEY_PAUSE }, /* Pause */ + { 0x2b, KEY_STOP }, /* Stop */ + { 0x67, KEY_FASTFORWARD }, /* Foward */ + { 0x66, KEY_REWIND }, /* Rewind */ + { 0x3e, KEY_SEARCH }, /* Auto Scan */ + { 0x2e, KEY_CAMERA }, /* Capture Video */ + { 0x6d, KEY_MENU }, /* Show/Hide Control */ + { 0x2f, KEY_ZOOM }, /* Full Screen */ + { 0x34, KEY_RADIO }, /* FM */ + { 0x65, KEY_POWER }, /* Computer power */ +}; + +struct ir_scancode_table ir_codes_norwood_table = { + .scan = ir_codes_norwood, + .size = ARRAY_SIZE(ir_codes_norwood), +}; +EXPORT_SYMBOL_GPL(ir_codes_norwood_table); /* From reading the following remotes: * Zenith Universal 7 / TV Mode 807 / VCR Mode 837 * Hauppauge (from NOVA-CI-s box product) * This is a "middle of the road" approach, differences are noted */ -IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = { - [ 0x00 ] = KEY_0, - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, - [ 0x0a ] = KEY_ENTER, - [ 0x0b ] = KEY_RED, - [ 0x0c ] = KEY_POWER, /* RADIO on Hauppauge */ - [ 0x0d ] = KEY_MUTE, - [ 0x0f ] = KEY_A, /* TV on Hauppauge */ - [ 0x10 ] = KEY_VOLUMEUP, - [ 0x11 ] = KEY_VOLUMEDOWN, - [ 0x14 ] = KEY_B, - [ 0x1c ] = KEY_UP, - [ 0x1d ] = KEY_DOWN, - [ 0x1e ] = KEY_OPTION, /* RESERVED on Hauppauge */ - [ 0x1f ] = KEY_BREAK, - [ 0x20 ] = KEY_CHANNELUP, - [ 0x21 ] = KEY_CHANNELDOWN, - [ 0x22 ] = KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */ - [ 0x24 ] = KEY_RESTART, - [ 0x25 ] = KEY_OK, - [ 0x26 ] = KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */ - [ 0x28 ] = KEY_ENTER, /* VCR mode on Zenith */ - [ 0x29 ] = KEY_PAUSE, - [ 0x2b ] = KEY_RIGHT, - [ 0x2c ] = KEY_LEFT, - [ 0x2e ] = KEY_MENU, /* FULL SCREEN on Hauppauge */ - [ 0x30 ] = KEY_SLOW, - [ 0x31 ] = KEY_PREVIOUS, /* VCR mode on Zenith */ - [ 0x32 ] = KEY_REWIND, - [ 0x34 ] = KEY_FASTFORWARD, - [ 0x35 ] = KEY_PLAY, - [ 0x36 ] = KEY_STOP, - [ 0x37 ] = KEY_RECORD, - [ 0x38 ] = KEY_TUNER, /* TV/VCR on Zenith */ - [ 0x3a ] = KEY_C, - [ 0x3c ] = KEY_EXIT, - [ 0x3d ] = KEY_POWER2, - [ 0x3e ] = KEY_TUNER, -}; - -EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old); +static struct ir_scancode ir_codes_budget_ci_old[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_ENTER }, + { 0x0b, KEY_RED }, + { 0x0c, KEY_POWER }, /* RADIO on Hauppauge */ + { 0x0d, KEY_MUTE }, + { 0x0f, KEY_A }, /* TV on Hauppauge */ + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x14, KEY_B }, + { 0x1c, KEY_UP }, + { 0x1d, KEY_DOWN }, + { 0x1e, KEY_OPTION }, /* RESERVED on Hauppauge */ + { 0x1f, KEY_BREAK }, + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x22, KEY_PREVIOUS }, /* Prev Ch on Zenith, SOURCE on Hauppauge */ + { 0x24, KEY_RESTART }, + { 0x25, KEY_OK }, + { 0x26, KEY_CYCLEWINDOWS }, /* MINIMIZE on Hauppauge */ + { 0x28, KEY_ENTER }, /* VCR mode on Zenith */ + { 0x29, KEY_PAUSE }, + { 0x2b, KEY_RIGHT }, + { 0x2c, KEY_LEFT }, + { 0x2e, KEY_MENU }, /* FULL SCREEN on Hauppauge */ + { 0x30, KEY_SLOW }, + { 0x31, KEY_PREVIOUS }, /* VCR mode on Zenith */ + { 0x32, KEY_REWIND }, + { 0x34, KEY_FASTFORWARD }, + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, + { 0x38, KEY_TUNER }, /* TV/VCR on Zenith */ + { 0x3a, KEY_C }, + { 0x3c, KEY_EXIT }, + { 0x3d, KEY_POWER2 }, + { 0x3e, KEY_TUNER }, +}; + +struct ir_scancode_table ir_codes_budget_ci_old_table = { + .scan = ir_codes_budget_ci_old, + .size = ARRAY_SIZE(ir_codes_budget_ci_old), +}; +EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old_table); /* * Marc Fargas <telenieko@telenieko.com> * this is the remote control that comes with the asus p7131 * which has a label saying is "Model PC-39" */ -IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_asus_pc39[] = { /* Keys 0 to 9 */ - [ 0x15 ] = KEY_0, - [ 0x29 ] = KEY_1, - [ 0x2d ] = KEY_2, - [ 0x2b ] = KEY_3, - [ 0x09 ] = KEY_4, - [ 0x0d ] = KEY_5, - [ 0x0b ] = KEY_6, - [ 0x31 ] = KEY_7, - [ 0x35 ] = KEY_8, - [ 0x33 ] = KEY_9, - - [ 0x3e ] = KEY_RADIO, /* radio */ - [ 0x03 ] = KEY_MENU, /* dvd/menu */ - [ 0x2a ] = KEY_VOLUMEUP, - [ 0x19 ] = KEY_VOLUMEDOWN, - [ 0x37 ] = KEY_UP, - [ 0x3b ] = KEY_DOWN, - [ 0x27 ] = KEY_LEFT, - [ 0x2f ] = KEY_RIGHT, - [ 0x25 ] = KEY_VIDEO, /* video */ - [ 0x39 ] = KEY_AUDIO, /* music */ - - [ 0x21 ] = KEY_TV, /* tv */ - [ 0x1d ] = KEY_EXIT, /* back */ - [ 0x0a ] = KEY_CHANNELUP, /* channel / program + */ - [ 0x1b ] = KEY_CHANNELDOWN, /* channel / program - */ - [ 0x1a ] = KEY_ENTER, /* enter */ - - [ 0x06 ] = KEY_PAUSE, /* play/pause */ - [ 0x1e ] = KEY_PREVIOUS, /* rew */ - [ 0x26 ] = KEY_NEXT, /* forward */ - [ 0x0e ] = KEY_REWIND, /* backward << */ - [ 0x3a ] = KEY_FASTFORWARD, /* forward >> */ - [ 0x36 ] = KEY_STOP, - [ 0x2e ] = KEY_RECORD, /* recording */ - [ 0x16 ] = KEY_POWER, /* the button that reads "close" */ - - [ 0x11 ] = KEY_ZOOM, /* full screen */ - [ 0x13 ] = KEY_MACRO, /* recall */ - [ 0x23 ] = KEY_HOME, /* home */ - [ 0x05 ] = KEY_PVR, /* picture */ - [ 0x3d ] = KEY_MUTE, /* mute */ - [ 0x01 ] = KEY_DVD, /* dvd */ -}; - -EXPORT_SYMBOL_GPL(ir_codes_asus_pc39); + { 0x15, KEY_0 }, + { 0x29, KEY_1 }, + { 0x2d, KEY_2 }, + { 0x2b, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0d, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x31, KEY_7 }, + { 0x35, KEY_8 }, + { 0x33, KEY_9 }, + + { 0x3e, KEY_RADIO }, /* radio */ + { 0x03, KEY_MENU }, /* dvd/menu */ + { 0x2a, KEY_VOLUMEUP }, + { 0x19, KEY_VOLUMEDOWN }, + { 0x37, KEY_UP }, + { 0x3b, KEY_DOWN }, + { 0x27, KEY_LEFT }, + { 0x2f, KEY_RIGHT }, + { 0x25, KEY_VIDEO }, /* video */ + { 0x39, KEY_AUDIO }, /* music */ + + { 0x21, KEY_TV }, /* tv */ + { 0x1d, KEY_EXIT }, /* back */ + { 0x0a, KEY_CHANNELUP }, /* channel / program + */ + { 0x1b, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1a, KEY_ENTER }, /* enter */ + + { 0x06, KEY_PAUSE }, /* play/pause */ + { 0x1e, KEY_PREVIOUS }, /* rew */ + { 0x26, KEY_NEXT }, /* forward */ + { 0x0e, KEY_REWIND }, /* backward << */ + { 0x3a, KEY_FASTFORWARD }, /* forward >> */ + { 0x36, KEY_STOP }, + { 0x2e, KEY_RECORD }, /* recording */ + { 0x16, KEY_POWER }, /* the button that reads "close" */ + + { 0x11, KEY_ZOOM }, /* full screen */ + { 0x13, KEY_MACRO }, /* recall */ + { 0x23, KEY_HOME }, /* home */ + { 0x05, KEY_PVR }, /* picture */ + { 0x3d, KEY_MUTE }, /* mute */ + { 0x01, KEY_DVD }, /* dvd */ +}; + +struct ir_scancode_table ir_codes_asus_pc39_table = { + .scan = ir_codes_asus_pc39, + .size = ARRAY_SIZE(ir_codes_asus_pc39), +}; +EXPORT_SYMBOL_GPL(ir_codes_asus_pc39_table); /* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons Juan Pablo Sormani <sorman@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_encore_enltv[] = { /* Power button does nothing, neither in Windows app, although it sends data (used for BIOS wakeup?) */ - [ 0x0d ] = KEY_MUTE, - - [ 0x1e ] = KEY_TV, - [ 0x00 ] = KEY_VIDEO, - [ 0x01 ] = KEY_AUDIO, /* music */ - [ 0x02 ] = KEY_MHP, /* picture */ - - [ 0x1f ] = KEY_1, - [ 0x03 ] = KEY_2, - [ 0x04 ] = KEY_3, - [ 0x05 ] = KEY_4, - [ 0x1c ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x1d ] = KEY_9, - [ 0x0a ] = KEY_0, - - [ 0x09 ] = KEY_LIST, /* -/-- */ - [ 0x0b ] = KEY_LAST, /* recall */ - - [ 0x14 ] = KEY_HOME, /* win start menu */ - [ 0x15 ] = KEY_EXIT, /* exit */ - [ 0x16 ] = KEY_UP, - [ 0x12 ] = KEY_DOWN, - [ 0x0c ] = KEY_RIGHT, - [ 0x17 ] = KEY_LEFT, - - [ 0x18 ] = KEY_ENTER, /* OK */ - - [ 0x0e ] = KEY_ESC, - [ 0x13 ] = KEY_D, /* desktop */ - [ 0x11 ] = KEY_TAB, - [ 0x19 ] = KEY_SWITCHVIDEOMODE, /* switch */ - - [ 0x1a ] = KEY_MENU, - [ 0x1b ] = KEY_ZOOM, /* fullscreen */ - [ 0x44 ] = KEY_TIME, /* time shift */ - [ 0x40 ] = KEY_MODE, /* source */ - - [ 0x5a ] = KEY_RECORD, - [ 0x42 ] = KEY_PLAY, /* play/pause */ - [ 0x45 ] = KEY_STOP, - [ 0x43 ] = KEY_CAMERA, /* camera icon */ - - [ 0x48 ] = KEY_REWIND, - [ 0x4a ] = KEY_FASTFORWARD, - [ 0x49 ] = KEY_PREVIOUS, - [ 0x4b ] = KEY_NEXT, - - [ 0x4c ] = KEY_FAVORITES, /* tv wall */ - [ 0x4d ] = KEY_SOUND, /* DVD sound */ - [ 0x4e ] = KEY_LANGUAGE, /* DVD lang */ - [ 0x4f ] = KEY_TEXT, /* DVD text */ - - [ 0x50 ] = KEY_SLEEP, /* shutdown */ - [ 0x51 ] = KEY_MODE, /* stereo > main */ - [ 0x52 ] = KEY_SELECT, /* stereo > sap */ - [ 0x53 ] = KEY_PROG1, /* teletext */ - - - [ 0x59 ] = KEY_RED, /* AP1 */ - [ 0x41 ] = KEY_GREEN, /* AP2 */ - [ 0x47 ] = KEY_YELLOW, /* AP3 */ - [ 0x57 ] = KEY_BLUE, /* AP4 */ -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); + { 0x0d, KEY_MUTE }, + + { 0x1e, KEY_TV }, + { 0x00, KEY_VIDEO }, + { 0x01, KEY_AUDIO }, /* music */ + { 0x02, KEY_MHP }, /* picture */ + + { 0x1f, KEY_1 }, + { 0x03, KEY_2 }, + { 0x04, KEY_3 }, + { 0x05, KEY_4 }, + { 0x1c, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x1d, KEY_9 }, + { 0x0a, KEY_0 }, + + { 0x09, KEY_LIST }, /* -/-- */ + { 0x0b, KEY_LAST }, /* recall */ + + { 0x14, KEY_HOME }, /* win start menu */ + { 0x15, KEY_EXIT }, /* exit */ + { 0x16, KEY_CHANNELUP }, /* UP */ + { 0x12, KEY_CHANNELDOWN }, /* DOWN */ + { 0x0c, KEY_VOLUMEUP }, /* RIGHT */ + { 0x17, KEY_VOLUMEDOWN }, /* LEFT */ + + { 0x18, KEY_ENTER }, /* OK */ + + { 0x0e, KEY_ESC }, + { 0x13, KEY_CYCLEWINDOWS }, /* desktop */ + { 0x11, KEY_TAB }, + { 0x19, KEY_SWITCHVIDEOMODE }, /* switch */ + + { 0x1a, KEY_MENU }, + { 0x1b, KEY_ZOOM }, /* fullscreen */ + { 0x44, KEY_TIME }, /* time shift */ + { 0x40, KEY_MODE }, /* source */ + + { 0x5a, KEY_RECORD }, + { 0x42, KEY_PLAY }, /* play/pause */ + { 0x45, KEY_STOP }, + { 0x43, KEY_CAMERA }, /* camera icon */ + + { 0x48, KEY_REWIND }, + { 0x4a, KEY_FASTFORWARD }, + { 0x49, KEY_PREVIOUS }, + { 0x4b, KEY_NEXT }, + + { 0x4c, KEY_FAVORITES }, /* tv wall */ + { 0x4d, KEY_SOUND }, /* DVD sound */ + { 0x4e, KEY_LANGUAGE }, /* DVD lang */ + { 0x4f, KEY_TEXT }, /* DVD text */ + + { 0x50, KEY_SLEEP }, /* shutdown */ + { 0x51, KEY_MODE }, /* stereo > main */ + { 0x52, KEY_SELECT }, /* stereo > sap */ + { 0x53, KEY_PROG1 }, /* teletext */ + + + { 0x59, KEY_RED }, /* AP1 */ + { 0x41, KEY_GREEN }, /* AP2 */ + { 0x47, KEY_YELLOW }, /* AP3 */ + { 0x57, KEY_BLUE }, /* AP4 */ +}; + +struct ir_scancode_table ir_codes_encore_enltv_table = { + .scan = ir_codes_encore_enltv, + .size = ARRAY_SIZE(ir_codes_encore_enltv), +}; +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_table); /* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton Mauro Carvalho Chehab <mchehab@infradead.org> */ -IR_KEYTAB_TYPE ir_codes_encore_enltv2[IR_KEYTAB_SIZE] = { - [0x4c] = KEY_POWER2, - [0x4a] = KEY_TUNER, - [0x40] = KEY_1, - [0x60] = KEY_2, - [0x50] = KEY_3, - [0x70] = KEY_4, - [0x48] = KEY_5, - [0x68] = KEY_6, - [0x58] = KEY_7, - [0x78] = KEY_8, - [0x44] = KEY_9, - [0x54] = KEY_0, - - [0x64] = KEY_LAST, /* +100 */ - [0x4e] = KEY_AGAIN, /* Recall */ - - [0x6c] = KEY_SWITCHVIDEOMODE, /* Video Source */ - [0x5e] = KEY_MENU, - [0x56] = KEY_SCREEN, - [0x7a] = KEY_SETUP, - - [0x46] = KEY_MUTE, - [0x5c] = KEY_MODE, /* Stereo */ - [0x74] = KEY_INFO, - [0x7c] = KEY_CLEAR, - - [0x55] = KEY_UP, - [0x49] = KEY_DOWN, - [0x7e] = KEY_LEFT, - [0x59] = KEY_RIGHT, - [0x6a] = KEY_ENTER, - - [0x42] = KEY_VOLUMEUP, - [0x62] = KEY_VOLUMEDOWN, - [0x52] = KEY_CHANNELUP, - [0x72] = KEY_CHANNELDOWN, - - [0x41] = KEY_RECORD, - [0x51] = KEY_SHUFFLE, /* Snapshot */ - [0x75] = KEY_TIME, /* Timeshift */ - [0x71] = KEY_TV2, /* PIP */ - - [0x45] = KEY_REWIND, - [0x6f] = KEY_PAUSE, - [0x7d] = KEY_FORWARD, - [0x79] = KEY_STOP, -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2); +static struct ir_scancode ir_codes_encore_enltv2[] = { + { 0x4c, KEY_POWER2 }, + { 0x4a, KEY_TUNER }, + { 0x40, KEY_1 }, + { 0x60, KEY_2 }, + { 0x50, KEY_3 }, + { 0x70, KEY_4 }, + { 0x48, KEY_5 }, + { 0x68, KEY_6 }, + { 0x58, KEY_7 }, + { 0x78, KEY_8 }, + { 0x44, KEY_9 }, + { 0x54, KEY_0 }, + + { 0x64, KEY_LAST }, /* +100 */ + { 0x4e, KEY_AGAIN }, /* Recall */ + + { 0x6c, KEY_SWITCHVIDEOMODE }, /* Video Source */ + { 0x5e, KEY_MENU }, + { 0x56, KEY_SCREEN }, + { 0x7a, KEY_SETUP }, + + { 0x46, KEY_MUTE }, + { 0x5c, KEY_MODE }, /* Stereo */ + { 0x74, KEY_INFO }, + { 0x7c, KEY_CLEAR }, + + { 0x55, KEY_UP }, + { 0x49, KEY_DOWN }, + { 0x7e, KEY_LEFT }, + { 0x59, KEY_RIGHT }, + { 0x6a, KEY_ENTER }, + + { 0x42, KEY_VOLUMEUP }, + { 0x62, KEY_VOLUMEDOWN }, + { 0x52, KEY_CHANNELUP }, + { 0x72, KEY_CHANNELDOWN }, + + { 0x41, KEY_RECORD }, + { 0x51, KEY_CAMERA }, /* Snapshot */ + { 0x75, KEY_TIME }, /* Timeshift */ + { 0x71, KEY_TV2 }, /* PIP */ + + { 0x45, KEY_REWIND }, + { 0x6f, KEY_PAUSE }, + { 0x7d, KEY_FORWARD }, + { 0x79, KEY_STOP }, +}; + +struct ir_scancode_table ir_codes_encore_enltv2_table = { + .scan = ir_codes_encore_enltv2, + .size = ARRAY_SIZE(ir_codes_encore_enltv2), +}; +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2_table); /* for the Technotrend 1500 bundled remotes (grey and black): */ -IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = { - [ 0x01 ] = KEY_POWER, - [ 0x02 ] = KEY_SHUFFLE, /* ? double-arrow key */ - [ 0x03 ] = KEY_1, - [ 0x04 ] = KEY_2, - [ 0x05 ] = KEY_3, - [ 0x06 ] = KEY_4, - [ 0x07 ] = KEY_5, - [ 0x08 ] = KEY_6, - [ 0x09 ] = KEY_7, - [ 0x0a ] = KEY_8, - [ 0x0b ] = KEY_9, - [ 0x0c ] = KEY_0, - [ 0x0d ] = KEY_UP, - [ 0x0e ] = KEY_LEFT, - [ 0x0f ] = KEY_OK, - [ 0x10 ] = KEY_RIGHT, - [ 0x11 ] = KEY_DOWN, - [ 0x12 ] = KEY_INFO, - [ 0x13 ] = KEY_EXIT, - [ 0x14 ] = KEY_RED, - [ 0x15 ] = KEY_GREEN, - [ 0x16 ] = KEY_YELLOW, - [ 0x17 ] = KEY_BLUE, - [ 0x18 ] = KEY_MUTE, - [ 0x19 ] = KEY_TEXT, - [ 0x1a ] = KEY_MODE, /* ? TV/Radio */ - [ 0x21 ] = KEY_OPTION, - [ 0x22 ] = KEY_EPG, - [ 0x23 ] = KEY_CHANNELUP, - [ 0x24 ] = KEY_CHANNELDOWN, - [ 0x25 ] = KEY_VOLUMEUP, - [ 0x26 ] = KEY_VOLUMEDOWN, - [ 0x27 ] = KEY_SETUP, - [ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */ - [ 0x3b ] = KEY_PLAY, - [ 0x3c ] = KEY_STOP, - [ 0x3d ] = KEY_REWIND, - [ 0x3e ] = KEY_PAUSE, - [ 0x3f ] = KEY_FORWARD, -}; - -EXPORT_SYMBOL_GPL(ir_codes_tt_1500); +static struct ir_scancode ir_codes_tt_1500[] = { + { 0x01, KEY_POWER }, + { 0x02, KEY_SHUFFLE }, /* ? double-arrow key */ + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x06, KEY_4 }, + { 0x07, KEY_5 }, + { 0x08, KEY_6 }, + { 0x09, KEY_7 }, + { 0x0a, KEY_8 }, + { 0x0b, KEY_9 }, + { 0x0c, KEY_0 }, + { 0x0d, KEY_UP }, + { 0x0e, KEY_LEFT }, + { 0x0f, KEY_OK }, + { 0x10, KEY_RIGHT }, + { 0x11, KEY_DOWN }, + { 0x12, KEY_INFO }, + { 0x13, KEY_EXIT }, + { 0x14, KEY_RED }, + { 0x15, KEY_GREEN }, + { 0x16, KEY_YELLOW }, + { 0x17, KEY_BLUE }, + { 0x18, KEY_MUTE }, + { 0x19, KEY_TEXT }, + { 0x1a, KEY_MODE }, /* ? TV/Radio */ + { 0x21, KEY_OPTION }, + { 0x22, KEY_EPG }, + { 0x23, KEY_CHANNELUP }, + { 0x24, KEY_CHANNELDOWN }, + { 0x25, KEY_VOLUMEUP }, + { 0x26, KEY_VOLUMEDOWN }, + { 0x27, KEY_SETUP }, + { 0x3a, KEY_RECORD }, /* these keys are only in the black remote */ + { 0x3b, KEY_PLAY }, + { 0x3c, KEY_STOP }, + { 0x3d, KEY_REWIND }, + { 0x3e, KEY_PAUSE }, + { 0x3f, KEY_FORWARD }, +}; + +struct ir_scancode_table ir_codes_tt_1500_table = { + .scan = ir_codes_tt_1500, + .size = ARRAY_SIZE(ir_codes_tt_1500), +}; +EXPORT_SYMBOL_GPL(ir_codes_tt_1500_table); /* DViCO FUSION HDTV MCE remote */ -IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE] = { - - [ 0x0b ] = KEY_1, - [ 0x17 ] = KEY_2, - [ 0x1b ] = KEY_3, - [ 0x07 ] = KEY_4, - [ 0x50 ] = KEY_5, - [ 0x54 ] = KEY_6, - [ 0x48 ] = KEY_7, - [ 0x4c ] = KEY_8, - [ 0x58 ] = KEY_9, - [ 0x03 ] = KEY_0, - - [ 0x5e ] = KEY_OK, - [ 0x51 ] = KEY_UP, - [ 0x53 ] = KEY_DOWN, - [ 0x5b ] = KEY_LEFT, - [ 0x5f ] = KEY_RIGHT, - - [ 0x02 ] = KEY_TV, /* Labeled DTV on remote */ - [ 0x0e ] = KEY_MP3, - [ 0x1a ] = KEY_DVD, - [ 0x1e ] = KEY_FAVORITES, /* Labeled CPF on remote */ - [ 0x16 ] = KEY_SETUP, - [ 0x46 ] = KEY_POWER2, /* TV On/Off button on remote */ - [ 0x0a ] = KEY_EPG, /* Labeled Guide on remote */ - - [ 0x49 ] = KEY_BACK, - [ 0x59 ] = KEY_INFO, /* Labeled MORE on remote */ - [ 0x4d ] = KEY_MENU, /* Labeled DVDMENU on remote */ - [ 0x55 ] = KEY_CYCLEWINDOWS, /* Labeled ALT-TAB on remote */ - - [ 0x0f ] = KEY_PREVIOUSSONG, /* Labeled |<< REPLAY on remote */ - [ 0x12 ] = KEY_NEXTSONG, /* Labeled >>| SKIP on remote */ - [ 0x42 ] = KEY_ENTER, /* Labeled START with a green - * MS windows logo on remote */ - - [ 0x15 ] = KEY_VOLUMEUP, - [ 0x05 ] = KEY_VOLUMEDOWN, - [ 0x11 ] = KEY_CHANNELUP, - [ 0x09 ] = KEY_CHANNELDOWN, - - [ 0x52 ] = KEY_CAMERA, - [ 0x5a ] = KEY_TUNER, - [ 0x19 ] = KEY_OPEN, - - [ 0x13 ] = KEY_MODE, /* 4:3 16:9 select */ - [ 0x1f ] = KEY_ZOOM, - - [ 0x43 ] = KEY_REWIND, - [ 0x47 ] = KEY_PLAYPAUSE, - [ 0x4f ] = KEY_FASTFORWARD, - [ 0x57 ] = KEY_MUTE, - [ 0x0d ] = KEY_STOP, - [ 0x01 ] = KEY_RECORD, - [ 0x4e ] = KEY_POWER, -}; - -EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce); +static struct ir_scancode ir_codes_fusionhdtv_mce[] = { + + { 0x0b, KEY_1 }, + { 0x17, KEY_2 }, + { 0x1b, KEY_3 }, + { 0x07, KEY_4 }, + { 0x50, KEY_5 }, + { 0x54, KEY_6 }, + { 0x48, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x58, KEY_9 }, + { 0x03, KEY_0 }, + + { 0x5e, KEY_OK }, + { 0x51, KEY_UP }, + { 0x53, KEY_DOWN }, + { 0x5b, KEY_LEFT }, + { 0x5f, KEY_RIGHT }, + + { 0x02, KEY_TV }, /* Labeled DTV on remote */ + { 0x0e, KEY_MP3 }, + { 0x1a, KEY_DVD }, + { 0x1e, KEY_FAVORITES }, /* Labeled CPF on remote */ + { 0x16, KEY_SETUP }, + { 0x46, KEY_POWER2 }, /* TV On/Off button on remote */ + { 0x0a, KEY_EPG }, /* Labeled Guide on remote */ + + { 0x49, KEY_BACK }, + { 0x59, KEY_INFO }, /* Labeled MORE on remote */ + { 0x4d, KEY_MENU }, /* Labeled DVDMENU on remote */ + { 0x55, KEY_CYCLEWINDOWS }, /* Labeled ALT-TAB on remote */ + + { 0x0f, KEY_PREVIOUSSONG }, /* Labeled |<< REPLAY on remote */ + { 0x12, KEY_NEXTSONG }, /* Labeled >>| SKIP on remote */ + { 0x42, KEY_ENTER }, /* Labeled START with a green + MS windows logo on remote */ + + { 0x15, KEY_VOLUMEUP }, + { 0x05, KEY_VOLUMEDOWN }, + { 0x11, KEY_CHANNELUP }, + { 0x09, KEY_CHANNELDOWN }, + + { 0x52, KEY_CAMERA }, + { 0x5a, KEY_TUNER }, + { 0x19, KEY_OPEN }, + + { 0x13, KEY_MODE }, /* 4:3 16:9 select */ + { 0x1f, KEY_ZOOM }, + + { 0x43, KEY_REWIND }, + { 0x47, KEY_PLAYPAUSE }, + { 0x4f, KEY_FASTFORWARD }, + { 0x57, KEY_MUTE }, + { 0x0d, KEY_STOP }, + { 0x01, KEY_RECORD }, + { 0x4e, KEY_POWER }, +}; + +struct ir_scancode_table ir_codes_fusionhdtv_mce_table = { + .scan = ir_codes_fusionhdtv_mce, + .size = ARRAY_SIZE(ir_codes_fusionhdtv_mce), +}; +EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce_table); /* Pinnacle PCTV HD 800i mini remote */ -IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE] = { - - [0x0f] = KEY_1, - [0x15] = KEY_2, - [0x10] = KEY_3, - [0x18] = KEY_4, - [0x1b] = KEY_5, - [0x1e] = KEY_6, - [0x11] = KEY_7, - [0x21] = KEY_8, - [0x12] = KEY_9, - [0x27] = KEY_0, - - [0x24] = KEY_ZOOM, - [0x2a] = KEY_SUBTITLE, - - [0x00] = KEY_MUTE, - [0x01] = KEY_ENTER, /* Pinnacle Logo */ - [0x39] = KEY_POWER, - - [0x03] = KEY_VOLUMEUP, - [0x09] = KEY_VOLUMEDOWN, - [0x06] = KEY_CHANNELUP, - [0x0c] = KEY_CHANNELDOWN, - - [0x2d] = KEY_REWIND, - [0x30] = KEY_PLAYPAUSE, - [0x33] = KEY_FASTFORWARD, - [0x3c] = KEY_STOP, - [0x36] = KEY_RECORD, - [0x3f] = KEY_EPG, /* Labeled "?" */ -}; -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd); +static struct ir_scancode ir_codes_pinnacle_pctv_hd[] = { + + { 0x0f, KEY_1 }, + { 0x15, KEY_2 }, + { 0x10, KEY_3 }, + { 0x18, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x1e, KEY_6 }, + { 0x11, KEY_7 }, + { 0x21, KEY_8 }, + { 0x12, KEY_9 }, + { 0x27, KEY_0 }, + + { 0x24, KEY_ZOOM }, + { 0x2a, KEY_SUBTITLE }, + + { 0x00, KEY_MUTE }, + { 0x01, KEY_ENTER }, /* Pinnacle Logo */ + { 0x39, KEY_POWER }, + + { 0x03, KEY_VOLUMEUP }, + { 0x09, KEY_VOLUMEDOWN }, + { 0x06, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + + { 0x2d, KEY_REWIND }, + { 0x30, KEY_PLAYPAUSE }, + { 0x33, KEY_FASTFORWARD }, + { 0x3c, KEY_STOP }, + { 0x36, KEY_RECORD }, + { 0x3f, KEY_EPG }, /* Labeled "?" */ +}; + +struct ir_scancode_table ir_codes_pinnacle_pctv_hd_table = { + .scan = ir_codes_pinnacle_pctv_hd, + .size = ARRAY_SIZE(ir_codes_pinnacle_pctv_hd), +}; +EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd_table); /* * Igor Kuznetsov <igk72@ya.ru> @@ -2198,13 +2365,13 @@ EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd); * the button labels (several variants when appropriate) * helps to descide which keycodes to assign to the buttons. */ -IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_behold[] = { /* 0x1c 0x12 * * TV/FM POWER * * */ - [ 0x1c ] = KEY_TUNER, /*XXX KEY_TV KEY_RADIO */ - [ 0x12 ] = KEY_POWER, + { 0x1c, KEY_TUNER }, /* XXX KEY_TV / KEY_RADIO */ + { 0x12, KEY_POWER }, /* 0x01 0x02 0x03 * * 1 2 3 * @@ -2215,28 +2382,28 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { * 0x07 0x08 0x09 * * 7 8 9 * * */ - [ 0x01 ] = KEY_1, - [ 0x02 ] = KEY_2, - [ 0x03 ] = KEY_3, - [ 0x04 ] = KEY_4, - [ 0x05 ] = KEY_5, - [ 0x06 ] = KEY_6, - [ 0x07 ] = KEY_7, - [ 0x08 ] = KEY_8, - [ 0x09 ] = KEY_9, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, /* 0x0a 0x00 0x17 * * RECALL 0 MODE * * */ - [ 0x0a ] = KEY_AGAIN, - [ 0x00 ] = KEY_0, - [ 0x17 ] = KEY_MODE, + { 0x0a, KEY_AGAIN }, + { 0x00, KEY_0 }, + { 0x17, KEY_MODE }, /* 0x14 0x10 * * ASPECT FULLSCREEN * * */ - [ 0x14 ] = KEY_SCREEN, - [ 0x10 ] = KEY_ZOOM, + { 0x14, KEY_SCREEN }, + { 0x10, KEY_ZOOM }, /* 0x0b * * Up * @@ -2247,17 +2414,17 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { * 0x015 * * Down * * */ - [ 0x0b ] = KEY_CHANNELUP, /*XXX KEY_UP */ - [ 0x18 ] = KEY_VOLUMEDOWN, /*XXX KEY_LEFT */ - [ 0x16 ] = KEY_OK, /*XXX KEY_ENTER */ - [ 0x0c ] = KEY_VOLUMEUP, /*XXX KEY_RIGHT */ - [ 0x15 ] = KEY_CHANNELDOWN, /*XXX KEY_DOWN */ + { 0x0b, KEY_CHANNELUP }, + { 0x18, KEY_VOLUMEDOWN }, + { 0x16, KEY_OK }, /* XXX KEY_ENTER */ + { 0x0c, KEY_VOLUMEUP }, + { 0x15, KEY_CHANNELDOWN }, /* 0x11 0x0d * * MUTE INFO * * */ - [ 0x11 ] = KEY_MUTE, - [ 0x0d ] = KEY_INFO, + { 0x11, KEY_MUTE }, + { 0x0d, KEY_INFO }, /* 0x0f 0x1b 0x1a * * RECORD PLAY/PAUSE STOP * @@ -2266,30 +2433,34 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { *TELETEXT AUDIO SOURCE * * RED YELLOW * * */ - [ 0x0f ] = KEY_RECORD, - [ 0x1b ] = KEY_PLAYPAUSE, - [ 0x1a ] = KEY_STOP, - [ 0x0e ] = KEY_TEXT, - [ 0x1f ] = KEY_RED, /*XXX KEY_AUDIO */ - [ 0x1e ] = KEY_YELLOW, /*XXX KEY_SOURCE */ + { 0x0f, KEY_RECORD }, + { 0x1b, KEY_PLAYPAUSE }, + { 0x1a, KEY_STOP }, + { 0x0e, KEY_TEXT }, + { 0x1f, KEY_RED }, /*XXX KEY_AUDIO */ + { 0x1e, KEY_YELLOW }, /*XXX KEY_SOURCE */ /* 0x1d 0x13 0x19 * * SLEEP PREVIEW DVB * * GREEN BLUE * * */ - [ 0x1d ] = KEY_SLEEP, - [ 0x13 ] = KEY_GREEN, - [ 0x19 ] = KEY_BLUE, /*XXX KEY_SAT */ + { 0x1d, KEY_SLEEP }, + { 0x13, KEY_GREEN }, + { 0x19, KEY_BLUE }, /* XXX KEY_SAT */ /* 0x58 0x5c * * FREEZE SNAPSHOT * * */ - [ 0x58 ] = KEY_SLOW, - [ 0x5c ] = KEY_SAVE, + { 0x58, KEY_SLOW }, + { 0x5c, KEY_CAMERA }, }; -EXPORT_SYMBOL_GPL(ir_codes_behold); +struct ir_scancode_table ir_codes_behold_table = { + .scan = ir_codes_behold, + .size = ARRAY_SIZE(ir_codes_behold), +}; +EXPORT_SYMBOL_GPL(ir_codes_behold_table); /* Beholder Intl. Ltd. 2008 * Dmitry Belimov d.belimov@google.com @@ -2299,16 +2470,16 @@ EXPORT_SYMBOL_GPL(ir_codes_behold); * the button labels (several variants when appropriate) * helps to descide which keycodes to assign to the buttons. */ -IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_behold_columbus[] = { /* 0x13 0x11 0x1C 0x12 * * Mute Source TV/FM Power * * */ - [0x13] = KEY_MUTE, - [0x11] = KEY_PROPS, - [0x1C] = KEY_TUNER, /* KEY_TV/KEY_RADIO */ - [0x12] = KEY_POWER, + { 0x13, KEY_MUTE }, + { 0x11, KEY_PROPS }, + { 0x1C, KEY_TUNER }, /* KEY_TV/KEY_RADIO */ + { 0x12, KEY_POWER }, /* 0x01 0x02 0x03 0x0D * * 1 2 3 Stereo * @@ -2319,173 +2490,188 @@ IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = { * 0x07 0x08 0x09 0x10 * * 7 8 9 Zoom * * */ - [0x01] = KEY_1, - [0x02] = KEY_2, - [0x03] = KEY_3, - [0x0D] = KEY_SETUP, /* Setup key */ - [0x04] = KEY_4, - [0x05] = KEY_5, - [0x06] = KEY_6, - [0x19] = KEY_BOOKMARKS, /* Snapshot key */ - [0x07] = KEY_7, - [0x08] = KEY_8, - [0x09] = KEY_9, - [0x10] = KEY_ZOOM, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x0D, KEY_SETUP }, /* Setup key */ + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x19, KEY_CAMERA }, /* Snapshot key */ + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x10, KEY_ZOOM }, /* 0x0A 0x00 0x0B 0x0C * * RECALL 0 ChannelUp VolumeUp * * */ - [0x0A] = KEY_AGAIN, - [0x00] = KEY_0, - [0x0B] = KEY_CHANNELUP, - [0x0C] = KEY_VOLUMEUP, + { 0x0A, KEY_AGAIN }, + { 0x00, KEY_0 }, + { 0x0B, KEY_CHANNELUP }, + { 0x0C, KEY_VOLUMEUP }, /* 0x1B 0x1D 0x15 0x18 * * Timeshift Record ChannelDown VolumeDown * * */ - [0x1B] = KEY_REWIND, - [0x1D] = KEY_RECORD, - [0x15] = KEY_CHANNELDOWN, - [0x18] = KEY_VOLUMEDOWN, + { 0x1B, KEY_TIME }, + { 0x1D, KEY_RECORD }, + { 0x15, KEY_CHANNELDOWN }, + { 0x18, KEY_VOLUMEDOWN }, /* 0x0E 0x1E 0x0F 0x1A * * Stop Pause Previouse Next * * */ - [0x0E] = KEY_STOP, - [0x1E] = KEY_PAUSE, - [0x0F] = KEY_PREVIOUS, - [0x1A] = KEY_NEXT, + { 0x0E, KEY_STOP }, + { 0x1E, KEY_PAUSE }, + { 0x0F, KEY_PREVIOUS }, + { 0x1A, KEY_NEXT }, + +}; +struct ir_scancode_table ir_codes_behold_columbus_table = { + .scan = ir_codes_behold_columbus, + .size = ARRAY_SIZE(ir_codes_behold_columbus), }; -EXPORT_SYMBOL_GPL(ir_codes_behold_columbus); +EXPORT_SYMBOL_GPL(ir_codes_behold_columbus_table); /* * Remote control for the Genius TVGO A11MCE * Adrian Pardini <pardo.bsso@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE] = { +static struct ir_scancode ir_codes_genius_tvgo_a11mce[] = { /* Keys 0 to 9 */ - [0x48] = KEY_0, - [0x09] = KEY_1, - [0x1d] = KEY_2, - [0x1f] = KEY_3, - [0x19] = KEY_4, - [0x1b] = KEY_5, - [0x11] = KEY_6, - [0x17] = KEY_7, - [0x12] = KEY_8, - [0x16] = KEY_9, - - [0x54] = KEY_RECORD, /* recording */ - [0x06] = KEY_MUTE, /* mute */ - [0x10] = KEY_POWER, - [0x40] = KEY_LAST, /* recall */ - [0x4c] = KEY_CHANNELUP, /* channel / program + */ - [0x00] = KEY_CHANNELDOWN, /* channel / program - */ - [0x0d] = KEY_VOLUMEUP, - [0x15] = KEY_VOLUMEDOWN, - [0x4d] = KEY_OK, /* also labeled as Pause */ - [0x1c] = KEY_ZOOM, /* full screen and Stop*/ - [0x02] = KEY_MODE, /* AV Source or Rewind*/ - [0x04] = KEY_LIST, /* -/-- */ + { 0x48, KEY_0 }, + { 0x09, KEY_1 }, + { 0x1d, KEY_2 }, + { 0x1f, KEY_3 }, + { 0x19, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x11, KEY_6 }, + { 0x17, KEY_7 }, + { 0x12, KEY_8 }, + { 0x16, KEY_9 }, + + { 0x54, KEY_RECORD }, /* recording */ + { 0x06, KEY_MUTE }, /* mute */ + { 0x10, KEY_POWER }, + { 0x40, KEY_LAST }, /* recall */ + { 0x4c, KEY_CHANNELUP }, /* channel / program + */ + { 0x00, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x0d, KEY_VOLUMEUP }, + { 0x15, KEY_VOLUMEDOWN }, + { 0x4d, KEY_OK }, /* also labeled as Pause */ + { 0x1c, KEY_ZOOM }, /* full screen and Stop*/ + { 0x02, KEY_MODE }, /* AV Source or Rewind*/ + { 0x04, KEY_LIST }, /* -/-- */ /* small arrows above numbers */ - [0x1a] = KEY_NEXT, /* also Fast Forward */ - [0x0e] = KEY_PREVIOUS, /* also Rewind */ + { 0x1a, KEY_NEXT }, /* also Fast Forward */ + { 0x0e, KEY_PREVIOUS }, /* also Rewind */ /* these are in a rather non standard layout and have an alternate name written */ - [0x1e] = KEY_UP, /* Video Setting */ - [0x0a] = KEY_DOWN, /* Video Default */ - [0x05] = KEY_LEFT, /* Snapshot */ - [0x0c] = KEY_RIGHT, /* Hide Panel */ + { 0x1e, KEY_UP }, /* Video Setting */ + { 0x0a, KEY_DOWN }, /* Video Default */ + { 0x05, KEY_CAMERA }, /* Snapshot */ + { 0x0c, KEY_RIGHT }, /* Hide Panel */ /* Four buttons without label */ - [0x49] = KEY_RED, - [0x0b] = KEY_GREEN, - [0x13] = KEY_YELLOW, - [0x50] = KEY_BLUE, + { 0x49, KEY_RED }, + { 0x0b, KEY_GREEN }, + { 0x13, KEY_YELLOW }, + { 0x50, KEY_BLUE }, +}; + +struct ir_scancode_table ir_codes_genius_tvgo_a11mce_table = { + .scan = ir_codes_genius_tvgo_a11mce, + .size = ARRAY_SIZE(ir_codes_genius_tvgo_a11mce), }; -EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce); +EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce_table); /* * Remote control for Powercolor Real Angel 330 * Daniel Fraga <fragabr@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { - [0x38] = KEY_SWITCHVIDEOMODE, /* switch inputs */ - [0x0c] = KEY_MEDIA, /* Turn ON/OFF App */ - [0x00] = KEY_0, - [0x01] = KEY_1, - [0x02] = KEY_2, - [0x03] = KEY_3, - [0x04] = KEY_4, - [0x05] = KEY_5, - [0x06] = KEY_6, - [0x07] = KEY_7, - [0x08] = KEY_8, - [0x09] = KEY_9, - [0x0a] = KEY_DIGITS, /* single, double, tripple digit */ - [0x29] = KEY_PREVIOUS, /* previous channel */ - [0x12] = KEY_BRIGHTNESSUP, - [0x13] = KEY_BRIGHTNESSDOWN, - [0x2b] = KEY_MODE, /* stereo/mono */ - [0x2c] = KEY_TEXT, /* teletext */ - [0x20] = KEY_UP, /* channel up */ - [0x21] = KEY_DOWN, /* channel down */ - [0x10] = KEY_RIGHT, /* volume up */ - [0x11] = KEY_LEFT, /* volume down */ - [0x0d] = KEY_MUTE, - [0x1f] = KEY_RECORD, - [0x17] = KEY_PLAY, - [0x16] = KEY_PAUSE, - [0x0b] = KEY_STOP, - [0x27] = KEY_FASTFORWARD, - [0x26] = KEY_REWIND, - [0x1e] = KEY_SEARCH, /* autoscan */ - [0x0e] = KEY_SHUFFLE, /* snapshot */ - [0x2d] = KEY_SETUP, - [0x0f] = KEY_SCREEN, /* full screen */ - [0x14] = KEY_RADIO, /* FM radio */ - [0x25] = KEY_POWER, /* power */ -}; -EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); +static struct ir_scancode ir_codes_powercolor_real_angel[] = { + { 0x38, KEY_SWITCHVIDEOMODE }, /* switch inputs */ + { 0x0c, KEY_MEDIA }, /* Turn ON/OFF App */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_DIGITS }, /* single, double, tripple digit */ + { 0x29, KEY_PREVIOUS }, /* previous channel */ + { 0x12, KEY_BRIGHTNESSUP }, + { 0x13, KEY_BRIGHTNESSDOWN }, + { 0x2b, KEY_MODE }, /* stereo/mono */ + { 0x2c, KEY_TEXT }, /* teletext */ + { 0x20, KEY_CHANNELUP }, /* channel up */ + { 0x21, KEY_CHANNELDOWN }, /* channel down */ + { 0x10, KEY_VOLUMEUP }, /* volume up */ + { 0x11, KEY_VOLUMEDOWN }, /* volume down */ + { 0x0d, KEY_MUTE }, + { 0x1f, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x16, KEY_PAUSE }, + { 0x0b, KEY_STOP }, + { 0x27, KEY_FASTFORWARD }, + { 0x26, KEY_REWIND }, + { 0x1e, KEY_SEARCH }, /* autoscan */ + { 0x0e, KEY_CAMERA }, /* snapshot */ + { 0x2d, KEY_SETUP }, + { 0x0f, KEY_SCREEN }, /* full screen */ + { 0x14, KEY_RADIO }, /* FM radio */ + { 0x25, KEY_POWER }, /* power */ +}; + +struct ir_scancode_table ir_codes_powercolor_real_angel_table = { + .scan = ir_codes_powercolor_real_angel, + .size = ARRAY_SIZE(ir_codes_powercolor_real_angel), +}; +EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel_table); /* Kworld Plus TV Analog Lite PCI IR Mauro Carvalho Chehab <mchehab@infradead.org> */ -IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = { - [0x0c] = KEY_PROG1, /* Kworld key */ - [0x16] = KEY_CLOSECD, /* -> ) */ - [0x1d] = KEY_POWER2, - - [0x00] = KEY_1, - [0x01] = KEY_2, - [0x02] = KEY_3, /* Two keys have the same code: 3 and left */ - [0x03] = KEY_4, /* Two keys have the same code: 3 and right */ - [0x04] = KEY_5, - [0x05] = KEY_6, - [0x06] = KEY_7, - [0x07] = KEY_8, - [0x08] = KEY_9, - [0x0a] = KEY_0, - - [0x09] = KEY_AGAIN, - [0x14] = KEY_MUTE, - - [0x20] = KEY_UP, - [0x21] = KEY_DOWN, - [0x0b] = KEY_ENTER, - - [0x10] = KEY_CHANNELUP, - [0x11] = KEY_CHANNELDOWN, +static struct ir_scancode ir_codes_kworld_plus_tv_analog[] = { + { 0x0c, KEY_PROG1 }, /* Kworld key */ + { 0x16, KEY_CLOSECD }, /* -> ) */ + { 0x1d, KEY_POWER2 }, + + { 0x00, KEY_1 }, + { 0x01, KEY_2 }, + { 0x02, KEY_3 }, /* Two keys have the same code: 3 and left */ + { 0x03, KEY_4 }, /* Two keys have the same code: 3 and right */ + { 0x04, KEY_5 }, + { 0x05, KEY_6 }, + { 0x06, KEY_7 }, + { 0x07, KEY_8 }, + { 0x08, KEY_9 }, + { 0x0a, KEY_0 }, + + { 0x09, KEY_AGAIN }, + { 0x14, KEY_MUTE }, + + { 0x20, KEY_UP }, + { 0x21, KEY_DOWN }, + { 0x0b, KEY_ENTER }, + + { 0x10, KEY_CHANNELUP }, + { 0x11, KEY_CHANNELDOWN }, /* Couldn't map key left/key right since those conflict with '3' and '4' scancodes I dunno what the original driver does */ - [0x13] = KEY_VOLUMEUP, - [0x12] = KEY_VOLUMEDOWN, + { 0x13, KEY_VOLUMEUP }, + { 0x12, KEY_VOLUMEDOWN }, /* The lower part of the IR There are several duplicated keycodes there. @@ -2496,280 +2682,468 @@ IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = { Also, it is not related to the time between keyup and keydown. */ - [0x19] = KEY_PAUSE, /* Timeshift */ - [0x1a] = KEY_STOP, - [0x1b] = KEY_RECORD, + { 0x19, KEY_TIME}, /* Timeshift */ + { 0x1a, KEY_STOP}, + { 0x1b, KEY_RECORD}, - [0x22] = KEY_TEXT, + { 0x22, KEY_TEXT}, - [0x15] = KEY_AUDIO, /* ((*)) */ - [0x0f] = KEY_ZOOM, - [0x1c] = KEY_SHUFFLE, /* snapshot */ + { 0x15, KEY_AUDIO}, /* ((*)) */ + { 0x0f, KEY_ZOOM}, + { 0x1c, KEY_CAMERA}, /* snapshot */ - [0x18] = KEY_RED, /* B */ - [0x23] = KEY_GREEN, /* C */ + { 0x18, KEY_RED}, /* B */ + { 0x23, KEY_GREEN}, /* C */ }; -EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog); +struct ir_scancode_table ir_codes_kworld_plus_tv_analog_table = { + .scan = ir_codes_kworld_plus_tv_analog, + .size = ARRAY_SIZE(ir_codes_kworld_plus_tv_analog), +}; +EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog_table); /* Kaiomy TVnPC U2 Mauro Carvalho Chehab <mchehab@infradead.org> */ -IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE] = { - [0x43] = KEY_POWER2, - [0x01] = KEY_LIST, - [0x0b] = KEY_ZOOM, - [0x03] = KEY_POWER, - - [0x04] = KEY_1, - [0x08] = KEY_2, - [0x02] = KEY_3, - - [0x0f] = KEY_4, - [0x05] = KEY_5, - [0x06] = KEY_6, - - [0x0c] = KEY_7, - [0x0d] = KEY_8, - [0x0a] = KEY_9, - - [0x11] = KEY_0, - - [0x09] = KEY_CHANNELUP, - [0x07] = KEY_CHANNELDOWN, - - [0x0e] = KEY_VOLUMEUP, - [0x13] = KEY_VOLUMEDOWN, - - [0x10] = KEY_HOME, - [0x12] = KEY_ENTER, - - [0x14] = KEY_RECORD, - [0x15] = KEY_STOP, - [0x16] = KEY_PLAY, - [0x17] = KEY_MUTE, - - [0x18] = KEY_UP, - [0x19] = KEY_DOWN, - [0x1a] = KEY_LEFT, - [0x1b] = KEY_RIGHT, - - [0x1c] = KEY_RED, - [0x1d] = KEY_GREEN, - [0x1e] = KEY_YELLOW, - [0x1f] = KEY_BLUE, -}; -EXPORT_SYMBOL_GPL(ir_codes_kaiomy); - -IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { - [0x20] = KEY_LIST, - [0x00] = KEY_POWER, - [0x28] = KEY_1, - [0x18] = KEY_2, - [0x38] = KEY_3, - [0x24] = KEY_4, - [0x14] = KEY_5, - [0x34] = KEY_6, - [0x2c] = KEY_7, - [0x1c] = KEY_8, - [0x3c] = KEY_9, - [0x12] = KEY_SUBTITLE, - [0x22] = KEY_0, - [0x32] = KEY_REWIND, - [0x3a] = KEY_SHUFFLE, - [0x02] = KEY_PRINT, - [0x11] = KEY_CHANNELDOWN, - [0x31] = KEY_CHANNELUP, - [0x0c] = KEY_ZOOM, - [0x1e] = KEY_VOLUMEDOWN, - [0x3e] = KEY_VOLUMEUP, - [0x0a] = KEY_MUTE, - [0x04] = KEY_AUDIO, - [0x26] = KEY_RECORD, - [0x06] = KEY_PLAY, - [0x36] = KEY_STOP, - [0x16] = KEY_PAUSE, - [0x2e] = KEY_REWIND, - [0x0e] = KEY_FASTFORWARD, - [0x30] = KEY_TEXT, - [0x21] = KEY_GREEN, - [0x01] = KEY_BLUE, - [0x08] = KEY_EPG, - [0x2a] = KEY_MENU, -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d); +static struct ir_scancode ir_codes_kaiomy[] = { + { 0x43, KEY_POWER2}, + { 0x01, KEY_LIST}, + { 0x0b, KEY_ZOOM}, + { 0x03, KEY_POWER}, -/* Encore ENLTV-FM v5.3 - Mauro Carvalho Chehab <mchehab@infradead.org> - */ -IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE] = { - [0x10] = KEY_POWER2, - [0x06] = KEY_MUTE, - - [0x09] = KEY_1, - [0x1d] = KEY_2, - [0x1f] = KEY_3, - [0x19] = KEY_4, - [0x1b] = KEY_5, - [0x11] = KEY_6, - [0x17] = KEY_7, - [0x12] = KEY_8, - [0x16] = KEY_9, - [0x48] = KEY_0, - - [0x04] = KEY_LIST, /* -/-- */ - [0x40] = KEY_LAST, /* recall */ - - [0x02] = KEY_MODE, /* TV/AV */ - [0x05] = KEY_SHUFFLE, /* SNAPSHOT */ - - [0x4c] = KEY_CHANNELUP, /* UP */ - [0x00] = KEY_CHANNELDOWN, /* DOWN */ - [0x0d] = KEY_VOLUMEUP, /* RIGHT */ - [0x15] = KEY_VOLUMEDOWN, /* LEFT */ - [0x49] = KEY_ENTER, /* OK */ - - [0x54] = KEY_RECORD, - [0x4d] = KEY_PLAY, /* pause */ - - [0x1e] = KEY_UP, /* video setting */ - [0x0e] = KEY_RIGHT, /* <- */ - [0x1a] = KEY_LEFT, /* -> */ - - [0x0a] = KEY_DOWN, /* video default */ - [0x0c] = KEY_ZOOM, /* hide pannel */ - [0x47] = KEY_SLEEP, /* shutdown */ -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53); + { 0x04, KEY_1}, + { 0x08, KEY_2}, + { 0x02, KEY_3}, -/* Zogis Real Audio 220 - 32 keys IR */ -IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = { - [0x1c] = KEY_RADIO, - [0x12] = KEY_POWER2, + { 0x0f, KEY_4}, + { 0x05, KEY_5}, + { 0x06, KEY_6}, + + { 0x0c, KEY_7}, + { 0x0d, KEY_8}, + { 0x0a, KEY_9}, - [0x01] = KEY_1, - [0x02] = KEY_2, - [0x03] = KEY_3, - [0x04] = KEY_4, - [0x05] = KEY_5, - [0x06] = KEY_6, - [0x07] = KEY_7, - [0x08] = KEY_8, - [0x09] = KEY_9, - [0x00] = KEY_0, + { 0x11, KEY_0}, - [0x0c] = KEY_VOLUMEUP, - [0x18] = KEY_VOLUMEDOWN, - [0x0b] = KEY_CHANNELUP, - [0x15] = KEY_CHANNELDOWN, - [0x16] = KEY_ENTER, + { 0x09, KEY_CHANNELUP}, + { 0x07, KEY_CHANNELDOWN}, - [0x11] = KEY_LIST, /* Source */ - [0x0d] = KEY_AUDIO, /* stereo */ + { 0x0e, KEY_VOLUMEUP}, + { 0x13, KEY_VOLUMEDOWN}, - [0x0f] = KEY_PREVIOUS, /* Prev */ - [0x1b] = KEY_PAUSE, /* Timeshift */ - [0x1a] = KEY_NEXT, /* Next */ + { 0x10, KEY_HOME}, + { 0x12, KEY_ENTER}, - [0x0e] = KEY_STOP, - [0x1f] = KEY_PLAY, - [0x1e] = KEY_PLAYPAUSE, /* Pause */ + { 0x14, KEY_RECORD}, + { 0x15, KEY_STOP}, + { 0x16, KEY_PLAY}, + { 0x17, KEY_MUTE}, - [0x1d] = KEY_RECORD, - [0x13] = KEY_MUTE, - [0x19] = KEY_SHUFFLE, /* Snapshot */ + { 0x18, KEY_UP}, + { 0x19, KEY_DOWN}, + { 0x1a, KEY_LEFT}, + { 0x1b, KEY_RIGHT}, + { 0x1c, KEY_RED}, + { 0x1d, KEY_GREEN}, + { 0x1e, KEY_YELLOW}, + { 0x1f, KEY_BLUE}, +}; +struct ir_scancode_table ir_codes_kaiomy_table = { + .scan = ir_codes_kaiomy, + .size = ARRAY_SIZE(ir_codes_kaiomy), +}; +EXPORT_SYMBOL_GPL(ir_codes_kaiomy_table); + +static struct ir_scancode ir_codes_avermedia_a16d[] = { + { 0x20, KEY_LIST}, + { 0x00, KEY_POWER}, + { 0x28, KEY_1}, + { 0x18, KEY_2}, + { 0x38, KEY_3}, + { 0x24, KEY_4}, + { 0x14, KEY_5}, + { 0x34, KEY_6}, + { 0x2c, KEY_7}, + { 0x1c, KEY_8}, + { 0x3c, KEY_9}, + { 0x12, KEY_SUBTITLE}, + { 0x22, KEY_0}, + { 0x32, KEY_REWIND}, + { 0x3a, KEY_SHUFFLE}, + { 0x02, KEY_PRINT}, + { 0x11, KEY_CHANNELDOWN}, + { 0x31, KEY_CHANNELUP}, + { 0x0c, KEY_ZOOM}, + { 0x1e, KEY_VOLUMEDOWN}, + { 0x3e, KEY_VOLUMEUP}, + { 0x0a, KEY_MUTE}, + { 0x04, KEY_AUDIO}, + { 0x26, KEY_RECORD}, + { 0x06, KEY_PLAY}, + { 0x36, KEY_STOP}, + { 0x16, KEY_PAUSE}, + { 0x2e, KEY_REWIND}, + { 0x0e, KEY_FASTFORWARD}, + { 0x30, KEY_TEXT}, + { 0x21, KEY_GREEN}, + { 0x01, KEY_BLUE}, + { 0x08, KEY_EPG}, + { 0x2a, KEY_MENU}, +}; +struct ir_scancode_table ir_codes_avermedia_a16d_table = { + .scan = ir_codes_avermedia_a16d, + .size = ARRAY_SIZE(ir_codes_avermedia_a16d), }; -EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys); +EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d_table); + +/* Encore ENLTV-FM v5.3 + Mauro Carvalho Chehab <mchehab@infradead.org> + */ +static struct ir_scancode ir_codes_encore_enltv_fm53[] = { + { 0x10, KEY_POWER2}, + { 0x06, KEY_MUTE}, + + { 0x09, KEY_1}, + { 0x1d, KEY_2}, + { 0x1f, KEY_3}, + { 0x19, KEY_4}, + { 0x1b, KEY_5}, + { 0x11, KEY_6}, + { 0x17, KEY_7}, + { 0x12, KEY_8}, + { 0x16, KEY_9}, + { 0x48, KEY_0}, + + { 0x04, KEY_LIST}, /* -/-- */ + { 0x40, KEY_LAST}, /* recall */ + + { 0x02, KEY_MODE}, /* TV/AV */ + { 0x05, KEY_CAMERA}, /* SNAPSHOT */ + + { 0x4c, KEY_CHANNELUP}, /* UP */ + { 0x00, KEY_CHANNELDOWN}, /* DOWN */ + { 0x0d, KEY_VOLUMEUP}, /* RIGHT */ + { 0x15, KEY_VOLUMEDOWN}, /* LEFT */ + { 0x49, KEY_ENTER}, /* OK */ + + { 0x54, KEY_RECORD}, + { 0x4d, KEY_PLAY}, /* pause */ + + { 0x1e, KEY_MENU}, /* video setting */ + { 0x0e, KEY_RIGHT}, /* <- */ + { 0x1a, KEY_LEFT}, /* -> */ + + { 0x0a, KEY_CLEAR}, /* video default */ + { 0x0c, KEY_ZOOM}, /* hide pannel */ + { 0x47, KEY_SLEEP}, /* shutdown */ +}; +struct ir_scancode_table ir_codes_encore_enltv_fm53_table = { + .scan = ir_codes_encore_enltv_fm53, + .size = ARRAY_SIZE(ir_codes_encore_enltv_fm53), +}; +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53_table); + +/* Zogis Real Audio 220 - 32 keys IR */ +static struct ir_scancode ir_codes_real_audio_220_32_keys[] = { + { 0x1c, KEY_RADIO}, + { 0x12, KEY_POWER2}, + + { 0x01, KEY_1}, + { 0x02, KEY_2}, + { 0x03, KEY_3}, + { 0x04, KEY_4}, + { 0x05, KEY_5}, + { 0x06, KEY_6}, + { 0x07, KEY_7}, + { 0x08, KEY_8}, + { 0x09, KEY_9}, + { 0x00, KEY_0}, + + { 0x0c, KEY_VOLUMEUP}, + { 0x18, KEY_VOLUMEDOWN}, + { 0x0b, KEY_CHANNELUP}, + { 0x15, KEY_CHANNELDOWN}, + { 0x16, KEY_ENTER}, + + { 0x11, KEY_LIST}, /* Source */ + { 0x0d, KEY_AUDIO}, /* stereo */ + + { 0x0f, KEY_PREVIOUS}, /* Prev */ + { 0x1b, KEY_TIME}, /* Timeshift */ + { 0x1a, KEY_NEXT}, /* Next */ + + { 0x0e, KEY_STOP}, + { 0x1f, KEY_PLAY}, + { 0x1e, KEY_PLAYPAUSE}, /* Pause */ + + { 0x1d, KEY_RECORD}, + { 0x13, KEY_MUTE}, + { 0x19, KEY_CAMERA}, /* Snapshot */ + +}; +struct ir_scancode_table ir_codes_real_audio_220_32_keys_table = { + .scan = ir_codes_real_audio_220_32_keys, + .size = ARRAY_SIZE(ir_codes_real_audio_220_32_keys), +}; +EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys_table); /* ATI TV Wonder HD 600 USB Devin Heitmueller <devin.heitmueller@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = { - [0x00] = KEY_RECORD, /* Row 1 */ - [0x01] = KEY_PLAYPAUSE, - [0x02] = KEY_STOP, - [0x03] = KEY_POWER, - [0x04] = KEY_PREVIOUS, /* Row 2 */ - [0x05] = KEY_REWIND, - [0x06] = KEY_FORWARD, - [0x07] = KEY_NEXT, - [0x08] = KEY_EPG, /* Row 3 */ - [0x09] = KEY_HOME, - [0x0a] = KEY_MENU, - [0x0b] = KEY_CHANNELUP, - [0x0c] = KEY_BACK, /* Row 4 */ - [0x0d] = KEY_UP, - [0x0e] = KEY_INFO, - [0x0f] = KEY_CHANNELDOWN, - [0x10] = KEY_LEFT, /* Row 5 */ - [0x11] = KEY_SELECT, - [0x12] = KEY_RIGHT, - [0x13] = KEY_VOLUMEUP, - [0x14] = KEY_LAST, /* Row 6 */ - [0x15] = KEY_DOWN, - [0x16] = KEY_MUTE, - [0x17] = KEY_VOLUMEDOWN, -}; - -EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600); +static struct ir_scancode ir_codes_ati_tv_wonder_hd_600[] = { + { 0x00, KEY_RECORD}, /* Row 1 */ + { 0x01, KEY_PLAYPAUSE}, + { 0x02, KEY_STOP}, + { 0x03, KEY_POWER}, + { 0x04, KEY_PREVIOUS}, /* Row 2 */ + { 0x05, KEY_REWIND}, + { 0x06, KEY_FORWARD}, + { 0x07, KEY_NEXT}, + { 0x08, KEY_EPG}, /* Row 3 */ + { 0x09, KEY_HOME}, + { 0x0a, KEY_MENU}, + { 0x0b, KEY_CHANNELUP}, + { 0x0c, KEY_BACK}, /* Row 4 */ + { 0x0d, KEY_UP}, + { 0x0e, KEY_INFO}, + { 0x0f, KEY_CHANNELDOWN}, + { 0x10, KEY_LEFT}, /* Row 5 */ + { 0x11, KEY_SELECT}, + { 0x12, KEY_RIGHT}, + { 0x13, KEY_VOLUMEUP}, + { 0x14, KEY_LAST}, /* Row 6 */ + { 0x15, KEY_DOWN}, + { 0x16, KEY_MUTE}, + { 0x17, KEY_VOLUMEDOWN}, +}; +struct ir_scancode_table ir_codes_ati_tv_wonder_hd_600_table = { + .scan = ir_codes_ati_tv_wonder_hd_600, + .size = ARRAY_SIZE(ir_codes_ati_tv_wonder_hd_600), +}; +EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600_table); /* DVBWorld remotes Igor M. Liplianin <liplianin@me.by> */ -IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = { - [0x0a] = KEY_Q, /*power*/ - [0x0c] = KEY_M, /*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_PAGEUP, /*ch+*/ - [0x0f] = KEY_PAGEDOWN, /*ch-*/ - [0x1a] = KEY_O, /*vol+*/ - [0x0e] = KEY_Z, /*vol-*/ - [0x04] = KEY_R, /*rec*/ - [0x09] = KEY_D, /*fav*/ - [0x08] = KEY_BACKSPACE, /*rewind*/ - [0x07] = KEY_A, /*fast*/ - [0x0b] = KEY_P, /*pause*/ - [0x02] = KEY_ESC, /*cancel*/ - [0x03] = KEY_G, /*tab*/ - [0x00] = KEY_UP, /*up*/ - [0x1f] = KEY_ENTER, /*ok*/ - [0x01] = KEY_DOWN, /*down*/ - [0x05] = KEY_C, /*cap*/ - [0x06] = KEY_S, /*stop*/ - [0x40] = KEY_F, /*full*/ - [0x1e] = KEY_W, /*tvmode*/ - [0x1b] = KEY_B, /*recall*/ -}; -EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec); +static struct ir_scancode ir_codes_dm1105_nec[] = { + { 0x0a, KEY_POWER2}, /* power */ + { 0x0c, KEY_MUTE}, /* 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_CHANNELUP}, /* ch+ */ + { 0x0f, KEY_CHANNELDOWN}, /* ch- */ + { 0x1a, KEY_VOLUMEUP}, /* vol+ */ + { 0x0e, KEY_VOLUMEDOWN}, /* vol- */ + { 0x04, KEY_RECORD}, /* rec */ + { 0x09, KEY_CHANNEL}, /* fav */ + { 0x08, KEY_BACKSPACE}, /* rewind */ + { 0x07, KEY_FASTFORWARD}, /* fast */ + { 0x0b, KEY_PAUSE}, /* pause */ + { 0x02, KEY_ESC}, /* cancel */ + { 0x03, KEY_TAB}, /* tab */ + { 0x00, KEY_UP}, /* up */ + { 0x1f, KEY_ENTER}, /* ok */ + { 0x01, KEY_DOWN}, /* down */ + { 0x05, KEY_RECORD}, /* cap */ + { 0x06, KEY_STOP}, /* stop */ + { 0x40, KEY_ZOOM}, /* full */ + { 0x1e, KEY_TV}, /* tvmode */ + { 0x1b, KEY_B}, /* recall */ +}; +struct ir_scancode_table ir_codes_dm1105_nec_table = { + .scan = ir_codes_dm1105_nec, + .size = ARRAY_SIZE(ir_codes_dm1105_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); + +/* Terratec Cinergy Hybrid T USB XS + Devin Heitmueller <dheitmueller@linuxtv.org> + */ +static struct ir_scancode ir_codes_terratec_cinergy_xs[] = { + { 0x41, KEY_HOME}, + { 0x01, KEY_POWER}, + { 0x42, KEY_MENU}, + { 0x02, KEY_1}, + { 0x03, KEY_2}, + { 0x04, KEY_3}, + { 0x43, KEY_SUBTITLE}, + { 0x05, KEY_4}, + { 0x06, KEY_5}, + { 0x07, KEY_6}, + { 0x44, KEY_TEXT}, + { 0x08, KEY_7}, + { 0x09, KEY_8}, + { 0x0a, KEY_9}, + { 0x45, KEY_DELETE}, + { 0x0b, KEY_TUNER}, + { 0x0c, KEY_0}, + { 0x0d, KEY_MODE}, + { 0x46, KEY_TV}, + { 0x47, KEY_DVD}, + { 0x49, KEY_VIDEO}, + { 0x4b, KEY_AUX}, + { 0x10, KEY_UP}, + { 0x11, KEY_LEFT}, + { 0x12, KEY_OK}, + { 0x13, KEY_RIGHT}, + { 0x14, KEY_DOWN}, + { 0x0f, KEY_EPG}, + { 0x16, KEY_INFO}, + { 0x4d, KEY_BACKSPACE}, + { 0x1c, KEY_VOLUMEUP}, + { 0x4c, KEY_PLAY}, + { 0x1b, KEY_CHANNELUP}, + { 0x1e, KEY_VOLUMEDOWN}, + { 0x1d, KEY_MUTE}, + { 0x1f, KEY_CHANNELDOWN}, + { 0x17, KEY_RED}, + { 0x18, KEY_GREEN}, + { 0x19, KEY_YELLOW}, + { 0x1a, KEY_BLUE}, + { 0x58, KEY_RECORD}, + { 0x48, KEY_STOP}, + { 0x40, KEY_PAUSE}, + { 0x54, KEY_LAST}, + { 0x4e, KEY_REWIND}, + { 0x4f, KEY_FASTFORWARD}, + { 0x5c, KEY_NEXT}, +}; +struct ir_scancode_table ir_codes_terratec_cinergy_xs_table = { + .scan = ir_codes_terratec_cinergy_xs, + .size = ARRAY_SIZE(ir_codes_terratec_cinergy_xs), +}; +EXPORT_SYMBOL_GPL(ir_codes_terratec_cinergy_xs_table); /* EVGA inDtube Devin Heitmueller <devin.heitmueller@gmail.com> */ -IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE] = { - [0x12] = KEY_POWER, - [0x02] = KEY_MODE, /* TV */ - [0x14] = KEY_MUTE, - [0x1a] = KEY_CHANNELUP, - [0x16] = KEY_TV2, /* PIP */ - [0x1d] = KEY_VOLUMEUP, - [0x05] = KEY_CHANNELDOWN, - [0x0f] = KEY_PLAYPAUSE, - [0x19] = KEY_VOLUMEDOWN, - [0x1c] = KEY_REWIND, - [0x0d] = KEY_RECORD, - [0x18] = KEY_FORWARD, - [0x1e] = KEY_PREVIOUS, - [0x1b] = KEY_STOP, - [0x1f] = KEY_NEXT, - [0x13] = KEY_CAMERA, -}; -EXPORT_SYMBOL_GPL(ir_codes_evga_indtube); +static struct ir_scancode ir_codes_evga_indtube[] = { + { 0x12, KEY_POWER}, + { 0x02, KEY_MODE}, /* TV */ + { 0x14, KEY_MUTE}, + { 0x1a, KEY_CHANNELUP}, + { 0x16, KEY_TV2}, /* PIP */ + { 0x1d, KEY_VOLUMEUP}, + { 0x05, KEY_CHANNELDOWN}, + { 0x0f, KEY_PLAYPAUSE}, + { 0x19, KEY_VOLUMEDOWN}, + { 0x1c, KEY_REWIND}, + { 0x0d, KEY_RECORD}, + { 0x18, KEY_FORWARD}, + { 0x1e, KEY_PREVIOUS}, + { 0x1b, KEY_STOP}, + { 0x1f, KEY_NEXT}, + { 0x13, KEY_CAMERA}, +}; +struct ir_scancode_table ir_codes_evga_indtube_table = { + .scan = ir_codes_evga_indtube, + .size = ARRAY_SIZE(ir_codes_evga_indtube), +}; +EXPORT_SYMBOL_GPL(ir_codes_evga_indtube_table); + +static struct ir_scancode ir_codes_videomate_s350[] = { + { 0x00, KEY_TV}, + { 0x01, KEY_DVD}, + { 0x04, KEY_RECORD}, + { 0x05, KEY_VIDEO}, /* TV/Video */ + { 0x07, KEY_STOP}, + { 0x08, KEY_PLAYPAUSE}, + { 0x0a, KEY_REWIND}, + { 0x0f, KEY_FASTFORWARD}, + { 0x10, KEY_CHANNELUP}, + { 0x12, KEY_VOLUMEUP}, + { 0x13, KEY_CHANNELDOWN}, + { 0x14, KEY_MUTE}, + { 0x15, KEY_VOLUMEDOWN}, + { 0x16, KEY_1}, + { 0x17, KEY_2}, + { 0x18, KEY_3}, + { 0x19, KEY_4}, + { 0x1a, KEY_5}, + { 0x1b, KEY_6}, + { 0x1c, KEY_7}, + { 0x1d, KEY_8}, + { 0x1e, KEY_9}, + { 0x1f, KEY_0}, + { 0x21, KEY_SLEEP}, + { 0x24, KEY_ZOOM}, + { 0x25, KEY_LAST}, /* Recall */ + { 0x26, KEY_SUBTITLE}, /* CC */ + { 0x27, KEY_LANGUAGE}, /* MTS */ + { 0x29, KEY_CHANNEL}, /* SURF */ + { 0x2b, KEY_A}, + { 0x2c, KEY_B}, + { 0x2f, KEY_CAMERA}, /* Snapshot */ + { 0x23, KEY_RADIO}, + { 0x02, KEY_PREVIOUSSONG}, + { 0x06, KEY_NEXTSONG}, + { 0x03, KEY_EPG}, + { 0x09, KEY_SETUP}, + { 0x22, KEY_BACKSPACE}, + { 0x0c, KEY_UP}, + { 0x0e, KEY_DOWN}, + { 0x0b, KEY_LEFT}, + { 0x0d, KEY_RIGHT}, + { 0x11, KEY_ENTER}, + { 0x20, KEY_TEXT}, +}; +struct ir_scancode_table ir_codes_videomate_s350_table = { + .scan = ir_codes_videomate_s350, + .size = ARRAY_SIZE(ir_codes_videomate_s350), +}; +EXPORT_SYMBOL_GPL(ir_codes_videomate_s350_table); + +/* GADMEI UTV330+ RM008Z remote + Shine Liu <shinel@foxmail.com> + */ +static struct ir_scancode ir_codes_gadmei_rm008z[] = { + { 0x14, KEY_POWER2}, /* POWER OFF */ + { 0x0c, KEY_MUTE}, /* MUTE */ + + { 0x18, KEY_TV}, /* TV */ + { 0x0e, KEY_VIDEO}, /* AV */ + { 0x0b, KEY_AUDIO}, /* SV */ + { 0x0f, KEY_RADIO}, /* FM */ + + { 0x00, KEY_1}, + { 0x01, KEY_2}, + { 0x02, KEY_3}, + { 0x03, KEY_4}, + { 0x04, KEY_5}, + { 0x05, KEY_6}, + { 0x06, KEY_7}, + { 0x07, KEY_8}, + { 0x08, KEY_9}, + { 0x09, KEY_0}, + { 0x0a, KEY_INFO}, /* OSD */ + { 0x1c, KEY_BACKSPACE}, /* LAST */ + + { 0x0d, KEY_PLAY}, /* PLAY */ + { 0x1e, KEY_CAMERA}, /* SNAPSHOT */ + { 0x1a, KEY_RECORD}, /* RECORD */ + { 0x17, KEY_STOP}, /* STOP */ + + { 0x1f, KEY_UP}, /* UP */ + { 0x44, KEY_DOWN}, /* DOWN */ + { 0x46, KEY_TAB}, /* BACK */ + { 0x4a, KEY_ZOOM}, /* FULLSECREEN */ + + { 0x10, KEY_VOLUMEUP}, /* VOLUMEUP */ + { 0x11, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ + { 0x12, KEY_CHANNELUP}, /* CHANNELUP */ + { 0x13, KEY_CHANNELDOWN}, /* CHANNELDOWN */ + { 0x15, KEY_ENTER}, /* OK */ +}; +struct ir_scancode_table ir_codes_gadmei_rm008z_table = { + .scan = ir_codes_gadmei_rm008z, + .size = ARRAY_SIZE(ir_codes_gadmei_rm008z), +}; +EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table); diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c index 825aa1412e6..9f5dba244cb 100644 --- a/drivers/media/common/tuners/qt1010.c +++ b/drivers/media/common/tuners/qt1010.c @@ -64,24 +64,22 @@ static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) /* dump all registers */ static void qt1010_dump_regs(struct qt1010_priv *priv) { - char buf[52], buf2[4]; u8 reg, val; for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk("%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } if (qt1010_readreg(priv, reg, &val) == 0) - sprintf(buf2, "%02x ", val); + printk(KERN_CONT " %02x", val); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0x2f) break; } - printk("%s\n", buf); + printk(KERN_CONT "\n"); } static int qt1010_set_params(struct dvb_frontend *fe, diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index fc76c30e24f..155c93eb75d 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -210,7 +210,8 @@ int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) tda18271_i2c_gate_ctrl(fe, 0); if (ret != 1) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); + tda_err("ERROR: idx = 0x%x, len = %d, " + "i2c_transfer returned: %d\n", idx, len, ret); return (ret == 1 ? 0 : ret); } diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index b1093563015..64595112000 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -27,7 +27,7 @@ module_param_named(debug, tda18271_debug, int, 0644); MODULE_PARM_DESC(debug, "set debug level " "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))"); -static int tda18271_cal_on_startup; +static int tda18271_cal_on_startup = -1; module_param_named(cal, tda18271_cal_on_startup, int, 0644); MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup"); @@ -36,6 +36,27 @@ static LIST_HEAD(hybrid_tuner_instance_list); /*---------------------------------------------------------------------*/ +static int tda18271_toggle_output(struct dvb_frontend *fe, int standby) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0); + + if (tda_fail(ret)) + goto fail; + + tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n", + standby ? "standby" : "active", + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on", + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on"); +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + static inline int charge_pump_source(struct dvb_frontend *fe, int force) { struct tda18271_priv *priv = fe->tuner_priv; @@ -271,7 +292,7 @@ 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); + rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; regs[R_EB14] = approx + rfcal_comp; ret = tda18271_write_regs(fe, R_EB14, 1); @@ -800,7 +821,7 @@ static int tda18271_init(struct dvb_frontend *fe) mutex_lock(&priv->lock); - /* power up */ + /* full power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); if (tda_fail(ret)) goto fail; @@ -818,6 +839,21 @@ fail: return ret; } +static int tda18271_sleep(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret; + + mutex_lock(&priv->lock); + + /* enter standby mode, with required output features enabled */ + ret = tda18271_toggle_output(fe, 1); + + mutex_unlock(&priv->lock); + + return ret; +} + /* ------------------------------------------------------------------ */ static int tda18271_agc(struct dvb_frontend *fe) @@ -827,8 +863,9 @@ static int tda18271_agc(struct dvb_frontend *fe) switch (priv->config) { case 0: - /* no LNA */ - tda_dbg("no agc configuration provided\n"); + /* no external agc configuration required */ + if (tda18271_debug & DBG_ADV) + tda_dbg("no agc configuration provided\n"); break; case 3: /* switch with GPIO of saa713x */ @@ -1010,22 +1047,6 @@ fail: return ret; } -static int tda18271_sleep(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret; - - mutex_lock(&priv->lock); - - /* standby mode w/ slave tuner output - * & loop thru & xtal oscillator on */ - ret = tda18271_set_standby_mode(fe, 1, 0, 0); - - mutex_unlock(&priv->lock); - - return ret; -} - static int tda18271_release(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; @@ -1192,18 +1213,33 @@ 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; + } + priv->cal_initialized = false; mutex_init(&priv->lock); fe->tuner_priv = priv; - if (cfg) - priv->small_i2c = cfg->small_i2c; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1213,18 +1249,29 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_lock(&priv->lock); tda18271_init_regs(fe); - if ((tda18271_cal_on_startup) && (priv->id == TDA18271HDC2)) + if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2)) tda18271c2_rf_cal_init(fe); mutex_unlock(&priv->lock); break; + } default: /* existing tuner instance */ fe->tuner_priv = priv; - /* allow dvb driver to override i2c gate setting */ - if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG)) - priv->gate = cfg->gate; + /* allow dvb driver to override configuration settings */ + if (cfg) { + if (cfg->gate != TDA18271_GATE_ANALOG) + priv->gate = cfg->gate; + if (cfg->role) + priv->role = cfg->role; + if (cfg->config) + priv->config = cfg->config; + if (cfg->small_i2c) + priv->small_i2c = cfg->small_i2c; + if (cfg->output_opt) + priv->output_opt = cfg->output_opt; + } break; } diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index ab14ceb9e0c..e21fdeff3dd 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -962,10 +962,9 @@ struct tda18271_cid_target_map { static struct tda18271_cid_target_map tda18271_cid_target[] = { { .rfmax = 46000, .target = 0x04, .limit = 1800 }, { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, - { .rfmax = 79100, .target = 0x01, .limit = 4000 }, + { .rfmax = 70100, .target = 0x01, .limit = 4000 }, { .rfmax = 136800, .target = 0x18, .limit = 4000 }, { .rfmax = 156700, .target = 0x18, .limit = 4000 }, - { .rfmax = 156700, .target = 0x18, .limit = 4000 }, { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, { .rfmax = 345000, .target = 0x18, .limit = 4000 }, diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 74beb28806f..2bee229acd9 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -108,6 +108,7 @@ struct tda18271_priv { enum tda18271_role role; enum tda18271_i2c_gate gate; enum tda18271_ver id; + enum tda18271_output_options output_opt; unsigned int config; /* interface to saa713x / tda829x */ unsigned int tm_rfcal; @@ -137,17 +138,17 @@ extern int tda18271_debug; #define tda_printk(kern, fmt, arg...) \ printk(kern "%s: " fmt, __func__, ##arg) -#define dprintk(kern, lvl, fmt, arg...) do {\ +#define tda_dprintk(lvl, fmt, arg...) do {\ if (tda18271_debug & lvl) \ - tda_printk(kern, 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...) dprintk(KERN_DEBUG, DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) dprintk(KERN_DEBUG, DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) dprintk(KERN_DEBUG, DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) dprintk(KERN_DEBUG, DBG_CAL, fmt, ##arg) + tda_printk(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_fail(ret) \ ({ \ diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h index 53a9892a18d..323f2912128 100644 --- a/drivers/media/common/tuners/tda18271.h +++ b/drivers/media/common/tuners/tda18271.h @@ -67,6 +67,17 @@ enum tda18271_i2c_gate { TDA18271_GATE_DIGITAL, }; +enum tda18271_output_options { + /* slave tuner output & loop thru & xtal oscillator always on */ + TDA18271_OUTPUT_LT_XT_ON = 0, + + /* slave tuner output loop thru off */ + TDA18271_OUTPUT_LT_OFF = 1, + + /* xtal oscillator off */ + TDA18271_OUTPUT_XT_OFF = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -77,6 +88,12 @@ struct tda18271_config { /* use i2c gate provided by analog or digital demod */ enum tda18271_i2c_gate gate; + /* output options that can be disabled */ + enum tda18271_output_options output_opt; + + /* 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; diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index 149d54cdf7b..8abbcc5fcf9 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -144,6 +144,8 @@ static inline int tuner_stereo(const int type, const int status) case TUNER_LG_NTSC_TAPE: case TUNER_TCL_MF02GIP_5N: return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); + case TUNER_PHILIPS_FM1216MK5: + return status | TUNER_STEREO; default: return status & TUNER_STEREO; } @@ -508,6 +510,10 @@ static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) case TUNER_TCL_MF02GIP_5N: buffer[3] = 0x19; break; + case TUNER_PHILIPS_FM1216MK5: + buffer[2] = 0x88; + buffer[3] = 0x09; + break; case TUNER_TNF_5335MF: buffer[3] = 0x11; break; diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index 6a7f1a417c2..2b876f3988c 100644 --- a/drivers/media/common/tuners/tuner-types.c +++ b/drivers/media/common/tuners/tuner-types.c @@ -1301,6 +1301,42 @@ static struct tuner_params tuner_fq1216lme_mk3_params[] = { }, }; +/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */ + +static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = { + /* The datasheet specified channel ranges and the bandswitch byte */ + /* The control byte value of 0x8e is just a guess */ + { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels 2 - B */ + { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels C - W+11 */ + { 16 * 999.99 , 0x8e, 0x08, }, /* Channels W+12 - 69 */ +}; + +static struct tuner_params tuner_partsnic_pti_5nf05_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_partsnic_pti_5nf05_ranges, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges), + .cb_first_if_lower_freq = 1, /* not specified but safe to do */ + }, +}; + +/* --------- TUNER_PHILIPS_CU1216L - DVB-C NIM ------------------------- */ + +static struct tuner_range tuner_cu1216l_ranges[] = { + { 16 * 160.25 /*MHz*/, 0xce, 0x01 }, + { 16 * 444.25 /*MHz*/, 0xce, 0x02 }, + { 16 * 999.99 , 0xce, 0x04 }, +}; + +static struct tuner_params tuner_philips_cu1216l_params[] = { + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_cu1216l_ranges, + .count = ARRAY_SIZE(tuner_cu1216l_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1753,6 +1789,22 @@ struct tunertype tuners[] = { .params = tuner_fq1216lme_mk3_params, .count = ARRAY_SIZE(tuner_fq1216lme_mk3_params), }, + + [TUNER_PARTSNIC_PTI_5NF05] = { + .name = "Partsnic (Daewoo) PTI-5NF05", + .params = tuner_partsnic_pti_5nf05_params, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params), + }, + [TUNER_PHILIPS_CU1216L] = { + .name = "Philips CU1216L", + .params = tuner_philips_cu1216l_params, + .count = ARRAY_SIZE(tuner_philips_cu1216l_params), + .stepsize = 62500, + }, + [TUNER_NXP_TDA18271] = { + .name = "NXP TDA18271", + /* see tda18271-fe.c for details */ + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index aa20ce8cc66..f270e605da8 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -1119,8 +1119,8 @@ static int xc2028_sleep(struct dvb_frontend *fe) struct xc2028_data *priv = fe->tuner_priv; int rc = 0; - /* Avoid firmware reload on slow devices */ - if (no_poweroff) + /* Avoid firmware reload on slow devices or if PM disabled */ + if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h index 19de7928a74..a90c35d50ad 100644 --- a/drivers/media/common/tuners/tuner-xc2028.h +++ b/drivers/media/common/tuners/tuner-xc2028.h @@ -38,6 +38,7 @@ struct xc2028_ctrl { unsigned int input1:1; unsigned int vhfbw7:1; unsigned int uhfbw8:1; + unsigned int disable_power_mgmt:1; unsigned int demod; enum firmware_type type:2; }; diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index b0198691892..35d0817126e 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -2,6 +2,19 @@ # DVB device configuration # +config DVB_MAX_ADAPTERS + int "maximum number of DVB/ATSC adapters" + depends on DVB_CORE + default 8 + range 1 255 + help + Maximum number of DVB/ATSC adapters. Increasing this number + increases the memory consumption of the DVB subsystem even + if a much lower number of DVB/ATSC adapters is present. + Only values in the range 4-32 are tested. + + If you are unsure about this, use the default value 8 + config DVB_DYNAMIC_MINORS bool "Dynamic DVB minor allocation" depends on DVB_CORE @@ -55,6 +68,10 @@ comment "Supported FireWire (IEEE 1394) Adapters" depends on DVB_CORE && IEEE1394 source "drivers/media/dvb/firewire/Kconfig" +comment "Supported Earthsoft PT1 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/dvb/pt1/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index 6092a5bb5a7..16d262ddb45 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -2,6 +2,6 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ pt1/ obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 9a6307a347b..850a6c60675 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -66,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) #endif /* SkyStar2 DVB-S rev 2.3 */ -#if FE_SUPPORTED(MT312) +#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ @@ -155,55 +155,34 @@ static struct mt312_config skystar23_samsung_tbdu18132_config = { .demod_address = 0x0e, }; -static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 buf[4]; - u32 div; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, - .len = sizeof(buf) }; - struct flexcop_device *fc = fe->dvb->priv; - div = (params->frequency + (125/2)) / 125; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = (div >> 0) & 0xff; - buf[2] = 0x84 | ((div >> 10) & 0x60); - buf[3] = 0x80; - - if (params->frequency < 1550000) - buf[3] |= 0x02; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - static int skystar2_rev23_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { + struct dvb_frontend_ops *ops; + fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); - if (fc->fe != NULL) { - struct dvb_frontend_ops *ops = &fc->fe->ops; - ops->tuner_ops.set_params = - skystar23_samsung_tbdu18132_tuner_set_params; - ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; - ops->diseqc_send_burst = flexcop_diseqc_send_burst; - ops->set_tone = flexcop_set_tone; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBDU18132)) + return 0; + + ops = &fc->fe->ops; + ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; + ops->diseqc_send_burst = flexcop_diseqc_send_burst; + ops->set_tone = flexcop_set_tone; + ops->set_voltage = flexcop_set_voltage; + fc->fe_sleep = ops->sleep; + ops->sleep = flexcop_sleep; + return 1; } #else #define skystar2_rev23_attach NULL #endif /* SkyStar2 DVB-S rev 2.6 */ -#if FE_SUPPORTED(STV0299) +#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -232,31 +211,6 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, return 0; } -static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 buf[4]; - u32 div; - struct i2c_msg msg = { - .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - struct flexcop_device *fc = fe->dvb->priv; - div = params->frequency / 125; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x84; /* 0xC4 */ - buf[3] = 0x08; - - if (params->frequency < 1500000) - buf[3] |= 0x10; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - static u8 samsung_tbmu24112_inittab[] = { 0x01, 0x15, 0x02, 0x30, @@ -318,15 +272,18 @@ static int skystar2_rev26_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); - if (fc->fe != NULL) { - struct dvb_frontend_ops *ops = &fc->fe->ops; - ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBMU24112)) + return 0; + + fc->fe->ops.set_voltage = flexcop_set_voltage; + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + return 1; + } #else #define skystar2_rev26_attach NULL @@ -421,7 +378,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, if (!fc->fe) return 0; - i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);; + i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe); if (!i2c_tuner) return 0; @@ -449,7 +406,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, #endif /* AirStar DVB-T */ -#if FE_SUPPORTED(MT352) +#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) { static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; @@ -467,32 +424,6 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) return 0; } -static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) -{ - u32 div; - unsigned char bs = 0; - - if (buf_len < 5) - return -EINVAL; - -#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ - div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - if (params->frequency >= 48000000 && params->frequency <= 154000000) \ - bs = 0x09; - if (params->frequency >= 161000000 && params->frequency <= 439000000) \ - bs = 0x0a; - if (params->frequency >= 447000000 && params->frequency <= 863000000) \ - bs = 0x08; - - pllbuf[0] = 0x61; - pllbuf[1] = div >> 8; - pllbuf[2] = div & 0xff; - pllbuf[3] = 0xcc; - pllbuf[4] = bs; - return 5; -} - static struct mt352_config samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = samsung_tdtc9251dh0_demod_init, @@ -502,11 +433,11 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); - if (fc->fe != NULL) { - fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TDTC9251DH0); } #else #define airstar_dvbt_attach NULL @@ -580,54 +511,7 @@ static int airstar_atsc3_attach(struct flexcop_device *fc, #endif /* CableStar2 DVB-C */ -#if FE_SUPPORTED(STV0297) -static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) -{ - struct flexcop_device *fc = fe->dvb->priv; - u8 buf[4]; - u16 div; - int ret; - -/* 62.5 kHz * 10 */ -#define REF_FREQ 625 -#define FREQ_OFFSET 36125 - - div = ((fep->frequency/1000 + FREQ_OFFSET) * 10) / REF_FREQ; -/* 4 MHz = 4000 KHz */ - - buf[0] = (u8)( div >> 8) & 0x7f; - buf[1] = (u8) div & 0xff; - -/* F(osc) = N * Reference Freq. (62.5 kHz) - * byte 2 : 0 N14 N13 N12 N11 N10 N9 N8 - * byte 3 : N7 N6 N5 N4 N3 N2 N1 N0 - * byte 4 : 1 * * AGD R3 R2 R1 R0 - * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 - * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */ - buf[2] = 0x95; - -/* Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 - * 47 - 153 0 * 0 0 0 0 0 1 0x01 - * 153 - 430 0 * 0 0 0 0 1 0 0x02 - * 430 - 822 0 * 0 0 1 0 0 0 0x08 - * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ - - if (fep->frequency <= 153000000) buf[3] = 0x01; - else if (fep->frequency <= 430000000) buf[3] = 0x02; - else if (fep->frequency <= 822000000) buf[3] = 0x08; - else buf[3] = 0x88; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n", fep->frequency, - buf[0], buf[1], buf[2], buf[3]); - ret = fc->i2c_request(&fc->fc_i2c_adap[2], - FC_WRITE, 0x61, buf[0], &buf[1], 3); - deb_tuner("tuner write returned: %d\n",ret); - return ret; -} - +#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) static u8 alps_tdee4_stv0297_inittab[] = { 0x80, 0x01, 0x80, 0x00, @@ -711,13 +595,25 @@ static int cablestar2_attach(struct flexcop_device *fc, { fc->fc_i2c_adap[0].no_base_addr = 1; fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); - if (!fc->fe) { - /* Reset for next frontend to try */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; - } - fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; + if (!fc->fe) + goto fail; + + /* This tuner doesn't use the stv0297's I2C gate, but instead the + * tuner is connected to a different flexcop I2C adapter. */ + if (fc->fe->ops.i2c_gate_ctrl) + fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); + fc->fe->ops.i2c_gate_ctrl = NULL; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, + &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) + goto fail; + return 1; + +fail: + /* Reset for next frontend to try */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; } #else #define cablestar2_attach NULL diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index fec1d77fa85..91353a6faf1 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -1059,7 +1059,7 @@ static int dst_get_tuner_info(struct dst_state *state) dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); } if (state->board_info[0] == 0xbc) { - if (state->type_flags != DST_TYPE_IS_ATSC) + if (state->dst_type != DST_TYPE_IS_ATSC) state->type_flags |= DST_TYPE_HAS_TS188; else state->type_flags |= DST_TYPE_HAS_NEWTUNE_2; diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 4dbd7d4185a..2d099e27175 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -44,6 +44,14 @@ #include "cx24116.h" #include "z0194a.h" +#define UNSET (-1U) + +#define DM1105_BOARD_NOAUTO UNSET +#define DM1105_BOARD_UNKNOWN 0 +#define DM1105_BOARD_DVBWORLD_2002 1 +#define DM1105_BOARD_DVBWORLD_2004 2 +#define DM1105_BOARD_AXESS_DM05 3 + /* ----------------------------------------------- */ /* * PCI ID's @@ -153,20 +161,105 @@ /* GPIO's for LNB power control */ #define DM1105_LNB_MASK 0x00000000 +#define DM1105_LNB_OFF 0x00020000 #define DM1105_LNB_13V 0x00010100 #define DM1105_LNB_18V 0x00000100 /* GPIO's for LNB power control for Axess DM05 */ #define DM05_LNB_MASK 0x00000000 +#define DM05_LNB_OFF 0x00020000/* actually 13v */ #define DM05_LNB_13V 0x00020000 #define DM05_LNB_18V 0x00030000 +static unsigned int card[] = {[0 ... 3] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + static int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +static unsigned int dm1105_devcount; + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +struct dm1105_board { + char *name; +}; + +struct dm1105_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +static const struct dm1105_board dm1105_boards[] = { + [DM1105_BOARD_UNKNOWN] = { + .name = "UNKNOWN/GENERIC", + }, + [DM1105_BOARD_DVBWORLD_2002] = { + .name = "DVBWorld PCI 2002", + }, + [DM1105_BOARD_DVBWORLD_2004] = { + .name = "DVBWorld PCI 2004", + }, + [DM1105_BOARD_AXESS_DM05] = { + .name = "Axess/EasyTv DM05", + }, +}; + +static const struct dm1105_subid dm1105_subids[] = { + { + .subvendor = 0x0000, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0001, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0000, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x0001, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x195d, + .subdevice = 0x1105, + .card = DM1105_BOARD_AXESS_DM05, + }, +}; + +static void dm1105_card_list(struct pci_dev *pci) +{ + int i; + + if (0 == pci->subsystem_vendor && + 0 == pci->subsystem_device) { + printk(KERN_ERR + "dm1105: Your board has no valid PCI Subsystem ID\n" + "dm1105: and thus can't be autodetected\n" + "dm1105: Please pass card=<n> insmod option to\n" + "dm1105: workaround that. Redirect complaints to\n" + "dm1105: the vendor of the TV card. Best regards,\n" + "dm1105: -- tux\n"); + } else { + printk(KERN_ERR + "dm1105: Your board isn't known (yet) to the driver.\n" + "dm1105: You can try to pick one of the existing\n" + "dm1105: card configs via card=<n> insmod option.\n" + "dm1105: Updating to the latest version might help\n" + "dm1105: as well.\n"); + } + printk(KERN_ERR "Here is a list of valid choices for the card=<n> " + "insmod option:\n"); + for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) + printk(KERN_ERR "dm1105: card=%d -> %s\n", + i, dm1105_boards[i].name); +} + /* infrared remote control */ struct infrared { struct input_dev *input_dev; @@ -193,6 +286,8 @@ struct dm1105dvb { struct dvb_frontend *fe; struct dvb_net dvbnet; unsigned int full_ts_users; + unsigned int boardnr; + int nr; /* i2c */ struct i2c_adapter i2c_adap; @@ -211,7 +306,6 @@ struct dm1105dvb { unsigned int PacketErrorCount; unsigned int dmarst; spinlock_t lock; - }; #define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg])) @@ -326,16 +420,20 @@ static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe) static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe); - u32 lnb_mask, lnb_13v, lnb_18v; + u32 lnb_mask, lnb_13v, lnb_18v, lnb_off; - switch (dm1105dvb->pdev->subsystem_device) { - case PCI_DEVICE_ID_DM05: + switch (dm1105dvb->boardnr) { + case DM1105_BOARD_AXESS_DM05: lnb_mask = DM05_LNB_MASK; + lnb_off = DM05_LNB_OFF; lnb_13v = DM05_LNB_13V; lnb_18v = DM05_LNB_18V; break; + case DM1105_BOARD_DVBWORLD_2002: + case DM1105_BOARD_DVBWORLD_2004: default: lnb_mask = DM1105_LNB_MASK; + lnb_off = DM1105_LNB_OFF; lnb_13v = DM1105_LNB_13V; lnb_18v = DM1105_LNB_18V; } @@ -343,8 +441,10 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta outl(lnb_mask, dm_io_mem(DM1105_GPIOCTR)); if (voltage == SEC_VOLTAGE_18) outl(lnb_18v , dm_io_mem(DM1105_GPIOVAL)); - else + else if (voltage == SEC_VOLTAGE_13) outl(lnb_13v, dm_io_mem(DM1105_GPIOVAL)); + else + outl(lnb_off, dm_io_mem(DM1105_GPIOVAL)); return 0; } @@ -477,7 +577,7 @@ static irqreturn_t dm1105dvb_irq(int irq, void *dev_id) int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) { struct input_dev *input_dev; - IR_KEYTAB_TYPE *ir_codes = ir_codes_dm1105_nec; + struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table; int ir_type = IR_TYPE_OTHER; int err = -ENOMEM; @@ -589,8 +689,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) { int ret; - switch (dm1105dvb->pdev->subsystem_device) { - case PCI_DEVICE_ID_DW2004: + switch (dm1105dvb->boardnr) { + case DM1105_BOARD_DVBWORLD_2004: dm1105dvb->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, &dm1105dvb->i2c_adap); @@ -598,6 +698,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; break; + case DM1105_BOARD_DVBWORLD_2002: + case DM1105_BOARD_AXESS_DM05: default: dm1105dvb->fe = dvb_attach( stv0299_attach, &sharp_z0194a_config, @@ -676,11 +778,31 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, struct dvb_demux *dvbdemux; struct dmx_demux *dmx; int ret = -ENOMEM; + int i; dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL); if (!dm1105dvb) return -ENOMEM; + /* board config */ + dm1105dvb->nr = dm1105_devcount; + dm1105dvb->boardnr = UNSET; + if (card[dm1105dvb->nr] < ARRAY_SIZE(dm1105_boards)) + dm1105dvb->boardnr = card[dm1105dvb->nr]; + for (i = 0; UNSET == dm1105dvb->boardnr && + i < ARRAY_SIZE(dm1105_subids); i++) + if (pdev->subsystem_vendor == + dm1105_subids[i].subvendor && + pdev->subsystem_device == + dm1105_subids[i].subdevice) + dm1105dvb->boardnr = dm1105_subids[i].card; + + if (UNSET == dm1105dvb->boardnr) { + dm1105dvb->boardnr = DM1105_BOARD_UNKNOWN; + dm1105_card_list(pdev); + } + + dm1105_devcount++; dm1105dvb->pdev = pdev; dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES; dm1105dvb->PacketErrorCount = 0; @@ -853,6 +975,7 @@ static void __devexit dm1105_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); + dm1105_devcount--; kfree(dm1105dvb); } @@ -861,17 +984,12 @@ static struct pci_device_id dm1105_id_table[] __devinitdata = { .vendor = PCI_VENDOR_ID_TRIGEM, .device = PCI_DEVICE_ID_DM1105, .subvendor = PCI_ANY_ID, - .subdevice = PCI_DEVICE_ID_DW2002, - }, { - .vendor = PCI_VENDOR_ID_TRIGEM, - .device = PCI_DEVICE_ID_DM1105, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_DEVICE_ID_DW2004, + .subdevice = PCI_ANY_ID, }, { .vendor = PCI_VENDOR_ID_AXESS, .device = PCI_DEVICE_ID_DM05, - .subvendor = PCI_VENDOR_ID_AXESS, - .subdevice = PCI_DEVICE_ID_DM05, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* empty */ }, diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 6d6121eb5d5..3750ff48cba 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -430,6 +430,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, /* stop feed but only mark the specified filter as stopped (state set) */ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) { + struct dmxdev_feed *feed; + dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); switch (dmxdevfilter->type) { @@ -438,7 +440,8 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); break; case DMXDEV_TYPE_PES: - dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); + list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) + feed->ts->stop_filtering(feed->ts); break; default: return -EINVAL; @@ -449,13 +452,23 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) /* start feed associated with the specified filter */ static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) { + struct dmxdev_feed *feed; + int ret; + dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); switch (filter->type) { case DMXDEV_TYPE_SEC: return filter->feed.sec->start_filtering(filter->feed.sec); case DMXDEV_TYPE_PES: - return filter->feed.ts->start_filtering(filter->feed.ts); + list_for_each_entry(feed, &filter->feed.ts, next) { + ret = feed->ts->start_filtering(feed->ts); + if (ret < 0) { + dvb_dmxdev_feed_stop(filter); + return ret; + } + } + break; default: return -EINVAL; } @@ -487,6 +500,9 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) { + struct dmxdev_feed *feed; + struct dmx_demux *demux; + if (dmxdevfilter->state < DMXDEV_STATE_GO) return 0; @@ -503,13 +519,12 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) dmxdevfilter->feed.sec = NULL; break; case DMXDEV_TYPE_PES: - if (!dmxdevfilter->feed.ts) - break; dvb_dmxdev_feed_stop(dmxdevfilter); - dmxdevfilter->dev->demux-> - release_ts_feed(dmxdevfilter->dev->demux, - dmxdevfilter->feed.ts); - dmxdevfilter->feed.ts = NULL; + demux = dmxdevfilter->dev->demux; + list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) { + demux->release_ts_feed(demux, feed->ts); + feed->ts = NULL; + } break; default: if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) @@ -521,19 +536,88 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) return 0; } +static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter) +{ + struct dmxdev_feed *feed, *tmp; + + /* delete all PIDs */ + list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) { + list_del(&feed->next); + kfree(feed); + } + + BUG_ON(!list_empty(&dmxdevfilter->feed.ts)); +} + static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) { if (dmxdevfilter->state < DMXDEV_STATE_SET) return 0; + if (dmxdevfilter->type == DMXDEV_TYPE_PES) + dvb_dmxdev_delete_pids(dmxdevfilter); + dmxdevfilter->type = DMXDEV_TYPE_NONE; dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); return 0; } +static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, + struct dmxdev_feed *feed) +{ + struct timespec timeout = { 0 }; + struct dmx_pes_filter_params *para = &filter->params.pes; + dmx_output_t otype; + int ret; + int ts_type; + enum dmx_ts_pes ts_pes; + struct dmx_ts_feed *tsfeed; + + feed->ts = NULL; + otype = para->output; + + ts_pes = (enum dmx_ts_pes)para->pes_type; + + if (ts_pes < DMX_PES_OTHER) + ts_type = TS_DECODER; + else + ts_type = 0; + + if (otype == DMX_OUT_TS_TAP) + ts_type |= TS_PACKET; + else if (otype == DMX_OUT_TSDEMUX_TAP) + ts_type |= TS_PACKET | TS_DEMUX; + else if (otype == DMX_OUT_TAP) + ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; + + ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts, + dvb_dmxdev_ts_callback); + if (ret < 0) + return ret; + + tsfeed = feed->ts; + tsfeed->priv = filter; + + ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout); + if (ret < 0) { + dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); + return ret; + } + + ret = tsfeed->start_filtering(tsfeed); + if (ret < 0) { + dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); + return ret; + } + + return 0; +} + static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) { struct dmxdev *dmxdev = filter->dev; + struct dmxdev_feed *feed; void *mem; int ret, i; @@ -631,56 +715,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) break; } case DMXDEV_TYPE_PES: - { - struct timespec timeout = { 0 }; - struct dmx_pes_filter_params *para = &filter->params.pes; - dmx_output_t otype; - int ts_type; - enum dmx_ts_pes ts_pes; - struct dmx_ts_feed **tsfeed = &filter->feed.ts; - - filter->feed.ts = NULL; - otype = para->output; - - ts_pes = (enum dmx_ts_pes)para->pes_type; - - if (ts_pes < DMX_PES_OTHER) - ts_type = TS_DECODER; - else - ts_type = 0; - - if (otype == DMX_OUT_TS_TAP) - ts_type |= TS_PACKET; - else if (otype == DMX_OUT_TSDEMUX_TAP) - ts_type |= TS_PACKET | TS_DEMUX; - else if (otype == DMX_OUT_TAP) - ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; - - ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, - tsfeed, - dvb_dmxdev_ts_callback); - if (ret < 0) - return ret; - - (*tsfeed)->priv = filter; - - ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, - 32768, timeout); - if (ret < 0) { - dmxdev->demux->release_ts_feed(dmxdev->demux, - *tsfeed); - return ret; - } - - ret = filter->feed.ts->start_filtering(filter->feed.ts); - if (ret < 0) { - dmxdev->demux->release_ts_feed(dmxdev->demux, - *tsfeed); - return ret; + list_for_each_entry(feed, &filter->feed.ts, next) { + ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); + if (ret < 0) { + dvb_dmxdev_filter_stop(filter); + return ret; + } } - break; - } default: return -EINVAL; } @@ -718,7 +760,7 @@ static int dvb_demux_open(struct inode *inode, struct file *file) dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); dmxdevfilter->type = DMXDEV_TYPE_NONE; dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); - dmxdevfilter->feed.ts = NULL; + INIT_LIST_HEAD(&dmxdevfilter->feed.ts); init_timer(&dmxdevfilter->timer); dvbdev->users++; @@ -760,6 +802,55 @@ static inline void invert_mode(dmx_filter_t *filter) filter->mode[i] ^= 0xff; } +static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, u16 pid) +{ + struct dmxdev_feed *feed; + + if ((filter->type != DMXDEV_TYPE_PES) || + (filter->state < DMXDEV_STATE_SET)) + return -EINVAL; + + /* only TS packet filters may have multiple PIDs */ + if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) && + (!list_empty(&filter->feed.ts))) + return -EINVAL; + + feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL); + if (feed == NULL) + return -ENOMEM; + + feed->pid = pid; + list_add(&feed->next, &filter->feed.ts); + + if (filter->state >= DMXDEV_STATE_GO) + return dvb_dmxdev_start_feed(dmxdev, filter, feed); + + return 0; +} + +static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, u16 pid) +{ + struct dmxdev_feed *feed, *tmp; + + if ((filter->type != DMXDEV_TYPE_PES) || + (filter->state < DMXDEV_STATE_SET)) + return -EINVAL; + + list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) { + if ((feed->pid == pid) && (feed->ts != NULL)) { + feed->ts->stop_filtering(feed->ts); + filter->dev->demux->release_ts_feed(filter->dev->demux, + feed->ts); + list_del(&feed->next); + kfree(feed); + } + } + + return 0; +} + static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter, struct dmx_sct_filter_params *params) @@ -784,7 +875,10 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter, struct dmx_pes_filter_params *params) { + int ret; + dvb_dmxdev_filter_stop(dmxdevfilter); + dvb_dmxdev_filter_reset(dmxdevfilter); if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) return -EINVAL; @@ -795,6 +889,11 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); + ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, + dmxdevfilter->params.pes.pid); + if (ret < 0) + return ret; + if (params->flags & DMX_IMMEDIATE_START) return dvb_dmxdev_filter_start(dmxdevfilter); @@ -958,6 +1057,24 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, &((struct dmx_stc *)parg)->base); break; + case DMX_ADD_PID: + if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { + ret = -ERESTARTSYS; + break; + } + ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg); + mutex_unlock(&dmxdevfilter->mutex); + break; + + case DMX_REMOVE_PID: + if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { + ret = -ERESTARTSYS; + break; + } + ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg); + mutex_unlock(&dmxdevfilter->mutex); + break; + default: ret = -EINVAL; break; diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h index 29746e70d32..c1379b56dfb 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.h +++ b/drivers/media/dvb/dvb-core/dmxdev.h @@ -53,13 +53,20 @@ enum dmxdev_state { DMXDEV_STATE_TIMEDOUT }; +struct dmxdev_feed { + u16 pid; + struct dmx_ts_feed *ts; + struct list_head next; +}; + struct dmxdev_filter { union { struct dmx_section_filter *sec; } filter; union { - struct dmx_ts_feed *ts; + /* list of TS and PES feeds (struct dmxdev_feed) */ + struct list_head ts; struct dmx_section_feed *sec; } feed; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index cfe2768d24a..eef6d361662 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -425,13 +425,9 @@ no_dvb_demux_tscheck: if ((DVR_FEED(feed)) && (dvr_done++)) continue; - if (feed->pid == pid) { + if (feed->pid == pid) dvb_dmx_swfilter_packet_type(feed, buf); - if (DVR_FEED(feed)) - continue; - } - - if (feed->pid == 0x2000) + else if (feed->pid == 0x2000) feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); } } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index f50ca7292a7..ddf639ed2fd 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -72,6 +72,7 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open( #define FESTATE_ZIGZAG_FAST 32 #define FESTATE_ZIGZAG_SLOW 64 #define FESTATE_DISEQC 128 +#define FESTATE_ERROR 256 #define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC) #define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST) #define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW) @@ -269,6 +270,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra { int autoinversion; int ready = 0; + int fe_set_err = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; int original_inversion = fepriv->parameters.inversion; u32 original_frequency = fepriv->parameters.frequency; @@ -345,7 +347,11 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra if (autoinversion) fepriv->parameters.inversion = fepriv->inversion; if (fe->ops.set_frontend) - fe->ops.set_frontend(fe, &fepriv->parameters); + fe_set_err = fe->ops.set_frontend(fe, &fepriv->parameters); + if (fe_set_err < 0) { + fepriv->state = FESTATE_ERROR; + return fe_set_err; + } fepriv->parameters.frequency = original_frequency; fepriv->parameters.inversion = original_inversion; @@ -357,6 +363,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra static void dvb_frontend_swzigzag(struct dvb_frontend *fe) { fe_status_t s = 0; + int retval = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; /* if we've got no parameters, just keep idling */ @@ -370,8 +377,12 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) { if (fepriv->state & FESTATE_RETUNE) { if (fe->ops.set_frontend) - fe->ops.set_frontend(fe, &fepriv->parameters); - fepriv->state = FESTATE_TUNED; + retval = fe->ops.set_frontend(fe, + &fepriv->parameters); + if (retval < 0) + fepriv->state = FESTATE_ERROR; + else + fepriv->state = FESTATE_TUNED; } fepriv->delay = 3*HZ; fepriv->quality = 0; @@ -449,7 +460,11 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) fepriv->delay = fepriv->min_delay; /* peform a tune */ - if (dvb_frontend_swzigzag_autotune(fe, fepriv->check_wrapped)) { + retval = dvb_frontend_swzigzag_autotune(fe, + fepriv->check_wrapped); + if (retval < 0) { + return; + } else if (retval) { /* OK, if we've run out of trials at the fast speed. * Drop back to slow for the _next_ attempt */ fepriv->state = FESTATE_SEARCHING_SLOW; @@ -823,9 +838,61 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, } } + /* check for supported modulation */ + if (fe->ops.info.type == FE_QAM && + (parms->u.qam.modulation > QAM_AUTO || + !((1 << (parms->u.qam.modulation + 10)) & fe->ops.info.caps))) { + printk(KERN_WARNING "DVB: adapter %i frontend %i modulation %u not supported\n", + fe->dvb->num, fe->id, parms->u.qam.modulation); + return -EINVAL; + } + return 0; } +static int dvb_frontend_clear_cache(struct dvb_frontend *fe) +{ + int i; + + memset(&(fe->dtv_property_cache), 0, + sizeof(struct dtv_frontend_properties)); + + fe->dtv_property_cache.state = DTV_CLEAR; + fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; + fe->dtv_property_cache.inversion = INVERSION_AUTO; + fe->dtv_property_cache.fec_inner = FEC_AUTO; + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; + fe->dtv_property_cache.bandwidth_hz = BANDWIDTH_AUTO; + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; + fe->dtv_property_cache.hierarchy = HIERARCHY_AUTO; + fe->dtv_property_cache.symbol_rate = QAM_AUTO; + fe->dtv_property_cache.code_rate_HP = FEC_AUTO; + fe->dtv_property_cache.code_rate_LP = FEC_AUTO; + + fe->dtv_property_cache.isdbt_partial_reception = -1; + fe->dtv_property_cache.isdbt_sb_mode = -1; + fe->dtv_property_cache.isdbt_sb_subchannel = -1; + fe->dtv_property_cache.isdbt_sb_segment_idx = -1; + fe->dtv_property_cache.isdbt_sb_segment_count = -1; + fe->dtv_property_cache.isdbt_layer_enabled = 0x7; + for (i = 0; i < 3; i++) { + fe->dtv_property_cache.layer[i].fec = FEC_AUTO; + fe->dtv_property_cache.layer[i].modulation = QAM_AUTO; + fe->dtv_property_cache.layer[i].interleaving = -1; + fe->dtv_property_cache.layer[i].segment_count = -1; + } + + return 0; +} + +#define _DTV_CMD(n, s, b) \ +[n] = { \ + .name = #n, \ + .cmd = n, \ + .set = s,\ + .buffer = b \ +} + static struct dtv_cmds_h dtv_cmds[] = { [DTV_TUNE] = { .name = "DTV_TUNE", @@ -925,6 +992,47 @@ static struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_TRANSMISSION_MODE, .set = 1, }, + + _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), + _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0), + + _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0), + _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0), + _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0), + _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0), + + _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), + /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -932,6 +1040,7 @@ static struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, + [DTV_API_VERSION] = { .name = "DTV_API_VERSION", .cmd = DTV_API_VERSION, @@ -1141,14 +1250,21 @@ static void dtv_property_adv_params_sync(struct dvb_frontend *fe) if(c->delivery_system == SYS_ISDBT) { /* Fake out a generic DVB-T request so we pass validation in the ioctl */ p->frequency = c->frequency; - p->inversion = INVERSION_AUTO; + p->inversion = c->inversion; p->u.ofdm.constellation = QAM_AUTO; p->u.ofdm.code_rate_HP = FEC_AUTO; p->u.ofdm.code_rate_LP = FEC_AUTO; - p->u.ofdm.bandwidth = BANDWIDTH_AUTO; p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; p->u.ofdm.hierarchy_information = HIERARCHY_AUTO; + if (c->bandwidth_hz == 8000000) + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + else if (c->bandwidth_hz == 7000000) + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + else if (c->bandwidth_hz == 6000000) + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + else + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; } } @@ -1250,6 +1366,65 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_HIERARCHY: tvp->u.data = fe->dtv_property_cache.hierarchy; break; + + /* ISDB-T Support here */ + case DTV_ISDBT_PARTIAL_RECEPTION: + tvp->u.data = fe->dtv_property_cache.isdbt_partial_reception; + break; + case DTV_ISDBT_SOUND_BROADCASTING: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_mode; + break; + case DTV_ISDBT_SB_SUBCHANNEL_ID: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_subchannel; + break; + case DTV_ISDBT_SB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_idx; + break; + case DTV_ISDBT_SB_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_count; + break; + case DTV_ISDBT_LAYER_ENABLED: + tvp->u.data = fe->dtv_property_cache.isdbt_layer_enabled; + break; + case DTV_ISDBT_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.layer[0].fec; + break; + case DTV_ISDBT_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[0].modulation; + break; + case DTV_ISDBT_LAYERA_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[0].segment_count; + break; + case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[0].interleaving; + break; + case DTV_ISDBT_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.layer[1].fec; + break; + case DTV_ISDBT_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[1].modulation; + break; + case DTV_ISDBT_LAYERB_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[1].segment_count; + break; + case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[1].interleaving; + break; + case DTV_ISDBT_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.layer[2].fec; + break; + case DTV_ISDBT_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.layer[2].modulation; + break; + case DTV_ISDBT_LAYERC_SEGMENT_COUNT: + tvp->u.data = fe->dtv_property_cache.layer[2].segment_count; + break; + case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: + tvp->u.data = fe->dtv_property_cache.layer[2].interleaving; + break; + case DTV_ISDBS_TS_ID: + tvp->u.data = fe->dtv_property_cache.isdbs_ts_id; + break; default: r = -1; } @@ -1278,10 +1453,8 @@ static int dtv_property_process_set(struct dvb_frontend *fe, /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ + dvb_frontend_clear_cache(fe); dprintk("%s() Flushing property cache\n", __func__); - memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); - fe->dtv_property_cache.state = tvp->cmd; - fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; break; case DTV_TUNE: /* interpret the cache of data, build either a traditional frontend @@ -1347,6 +1520,65 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_HIERARCHY: fe->dtv_property_cache.hierarchy = tvp->u.data; break; + + /* ISDB-T Support here */ + case DTV_ISDBT_PARTIAL_RECEPTION: + fe->dtv_property_cache.isdbt_partial_reception = tvp->u.data; + break; + case DTV_ISDBT_SOUND_BROADCASTING: + fe->dtv_property_cache.isdbt_sb_mode = tvp->u.data; + break; + case DTV_ISDBT_SB_SUBCHANNEL_ID: + fe->dtv_property_cache.isdbt_sb_subchannel = tvp->u.data; + break; + case DTV_ISDBT_SB_SEGMENT_IDX: + fe->dtv_property_cache.isdbt_sb_segment_idx = tvp->u.data; + break; + case DTV_ISDBT_SB_SEGMENT_COUNT: + fe->dtv_property_cache.isdbt_sb_segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYER_ENABLED: + fe->dtv_property_cache.isdbt_layer_enabled = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_FEC: + fe->dtv_property_cache.layer[0].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_MODULATION: + fe->dtv_property_cache.layer[0].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_SEGMENT_COUNT: + fe->dtv_property_cache.layer[0].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[0].interleaving = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_FEC: + fe->dtv_property_cache.layer[1].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_MODULATION: + fe->dtv_property_cache.layer[1].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_SEGMENT_COUNT: + fe->dtv_property_cache.layer[1].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[1].interleaving = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_FEC: + fe->dtv_property_cache.layer[2].fec = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_MODULATION: + fe->dtv_property_cache.layer[2].modulation = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_SEGMENT_COUNT: + fe->dtv_property_cache.layer[2].segment_count = tvp->u.data; + break; + case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: + fe->dtv_property_cache.layer[2].interleaving = tvp->u.data; + break; + case DTV_ISDBS_TS_ID: + fe->dtv_property_cache.isdbs_ts_id = tvp->u.data; + break; default: r = -1; } @@ -1499,7 +1731,8 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, /* if retune was requested but hasn't occured yet, prevent * that user get signal state from previous tuning */ - if(fepriv->state == FESTATE_RETUNE) { + if (fepriv->state == FESTATE_RETUNE || + fepriv->state == FESTATE_ERROR) { err=0; *status = 0; break; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index e176da472d7..810f07d6324 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -341,6 +341,23 @@ struct dtv_frontend_properties { fe_rolloff_t rolloff; fe_delivery_system_t delivery_system; + + /* ISDB-T specifics */ + u8 isdbt_partial_reception; + u8 isdbt_sb_mode; + u8 isdbt_sb_subchannel; + u32 isdbt_sb_segment_idx; + u32 isdbt_sb_segment_count; + u8 isdbt_layer_enabled; + struct { + u8 segment_count; + fe_code_rate_t fec; + fe_modulation_t modulation; + u8 interleaving; + } layer[3]; + + /* ISDB-T specifics */ + u32 isdbs_ts_id; }; struct dvb_frontend { diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 8280f8d66a3..8c9ae0a3a27 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -904,7 +904,7 @@ static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev) { dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index 479dd05762a..94159b90f73 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -447,7 +447,7 @@ static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -static char *dvb_nodename(struct device *dev) +static char *dvb_devnode(struct device *dev, mode_t *mode) { struct dvb_device *dvbdev = dev_get_drvdata(dev); @@ -478,7 +478,7 @@ static int __init init_dvbdev(void) goto error; } dvb_class->dev_uevent = dvb_uevent; - dvb_class->nodename = dvb_nodename; + dvb_class->devnode = dvb_devnode; return 0; error: diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 487919bea7a..01fc7048474 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -30,7 +30,11 @@ #define DVB_MAJOR 212 -#define DVB_MAX_ADAPTERS 8 +#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0 + #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS +#else + #define DVB_MAX_ADAPTERS 8 +#endif #define DVB_UNSET (-1) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 496c1a37034..9744b069241 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -71,10 +71,11 @@ config DVB_USB_DIB0700 depends on DVB_USB select DVB_DIB7000P if !DVB_FE_CUSTOMISE select DVB_DIB7000M if !DVB_FE_CUSTOMISE + select DVB_DIB8000 if !DVB_FE_CUSTOMISE select DVB_DIB3000MC if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select DVB_TUNER_DIB0070 select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE @@ -87,7 +88,7 @@ config DVB_USB_DIB0700 Avermedia and other big and small companies. For an up-to-date list of devices supported by this driver, have a look - on the Linux-DVB Wiki at www.linuxtv.org. + on the LinuxTV Wiki at www.linuxtv.org. Say Y if you own such a device and want to use it. You should build it as a module. @@ -253,7 +254,7 @@ config DVB_USB_AF9005_REMOTE Afatech AF9005 based receiver. config DVB_USB_DW2102 - tristate "DvbWorld DVB-S/S2 USB2.0 support" + tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE @@ -262,9 +263,11 @@ config DVB_USB_DW2102 select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_SI21XX if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_ZL10039 if !DVB_FE_CUSTOMISE help Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers - and the TeVii S650. + and the TeVii S650, S630. config DVB_USB_CINERGY_T2 tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" @@ -313,3 +316,9 @@ config DVB_USB_CE6230 select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver + +config DVB_USB_FRIIO + tristate "Friio ISDB-T USB2.0 Receiver support" + depends on DVB_USB + help + Say Y here to support the Japanese DTV receiver Friio. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index f92734ed777..85b83a43d55 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -79,6 +79,9 @@ obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o dvb-usb-ce6230-objs = ce6230.o obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o +dvb-usb-friio-objs = friio.o friio-fe.o +obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index dc8c8784caa..6247239982e 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -38,41 +38,41 @@ static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_pr } static struct dvb_usb_rc_key a800_rc_keys[] = { - { 0x02, 0x01, KEY_PROG1 }, /* SOURCE */ - { 0x02, 0x00, KEY_POWER }, /* POWER */ - { 0x02, 0x05, KEY_1 }, /* 1 */ - { 0x02, 0x06, KEY_2 }, /* 2 */ - { 0x02, 0x07, KEY_3 }, /* 3 */ - { 0x02, 0x09, KEY_4 }, /* 4 */ - { 0x02, 0x0a, KEY_5 }, /* 5 */ - { 0x02, 0x0b, KEY_6 }, /* 6 */ - { 0x02, 0x0d, KEY_7 }, /* 7 */ - { 0x02, 0x0e, KEY_8 }, /* 8 */ - { 0x02, 0x0f, KEY_9 }, /* 9 */ - { 0x02, 0x12, KEY_LEFT }, /* L / DISPLAY */ - { 0x02, 0x11, KEY_0 }, /* 0 */ - { 0x02, 0x13, KEY_RIGHT }, /* R / CH RTN */ - { 0x02, 0x17, KEY_PROG2 }, /* SNAP SHOT */ - { 0x02, 0x10, KEY_PROG3 }, /* 16-CH PREV */ - { 0x02, 0x1e, KEY_VOLUMEDOWN }, /* VOL DOWN */ - { 0x02, 0x0c, KEY_ZOOM }, /* FULL SCREEN */ - { 0x02, 0x1f, KEY_VOLUMEUP }, /* VOL UP */ - { 0x02, 0x14, KEY_MUTE }, /* MUTE */ - { 0x02, 0x08, KEY_AUDIO }, /* AUDIO */ - { 0x02, 0x19, KEY_RECORD }, /* RECORD */ - { 0x02, 0x18, KEY_PLAY }, /* PLAY */ - { 0x02, 0x1b, KEY_STOP }, /* STOP */ - { 0x02, 0x1a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ - { 0x02, 0x1d, KEY_BACK }, /* << / RED */ - { 0x02, 0x1c, KEY_FORWARD }, /* >> / YELLOW */ - { 0x02, 0x03, KEY_TEXT }, /* TELETEXT */ - { 0x02, 0x04, KEY_EPG }, /* EPG */ - { 0x02, 0x15, KEY_MENU }, /* MENU */ - - { 0x03, 0x03, KEY_CHANNELUP }, /* CH UP */ - { 0x03, 0x02, KEY_CHANNELDOWN }, /* CH DOWN */ - { 0x03, 0x01, KEY_FIRST }, /* |<< / GREEN */ - { 0x03, 0x00, KEY_LAST }, /* >>| / BLUE */ + { 0x0201, KEY_PROG1 }, /* SOURCE */ + { 0x0200, KEY_POWER }, /* POWER */ + { 0x0205, KEY_1 }, /* 1 */ + { 0x0206, KEY_2 }, /* 2 */ + { 0x0207, KEY_3 }, /* 3 */ + { 0x0209, KEY_4 }, /* 4 */ + { 0x020a, KEY_5 }, /* 5 */ + { 0x020b, KEY_6 }, /* 6 */ + { 0x020d, KEY_7 }, /* 7 */ + { 0x020e, KEY_8 }, /* 8 */ + { 0x020f, KEY_9 }, /* 9 */ + { 0x0212, KEY_LEFT }, /* L / DISPLAY */ + { 0x0211, KEY_0 }, /* 0 */ + { 0x0213, KEY_RIGHT }, /* R / CH RTN */ + { 0x0217, KEY_PROG2 }, /* SNAP SHOT */ + { 0x0210, KEY_PROG3 }, /* 16-CH PREV */ + { 0x021e, KEY_VOLUMEDOWN }, /* VOL DOWN */ + { 0x020c, KEY_ZOOM }, /* FULL SCREEN */ + { 0x021f, KEY_VOLUMEUP }, /* VOL UP */ + { 0x0214, KEY_MUTE }, /* MUTE */ + { 0x0208, KEY_AUDIO }, /* AUDIO */ + { 0x0219, KEY_RECORD }, /* RECORD */ + { 0x0218, KEY_PLAY }, /* PLAY */ + { 0x021b, KEY_STOP }, /* STOP */ + { 0x021a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ + { 0x021d, KEY_BACK }, /* << / RED */ + { 0x021c, KEY_FORWARD }, /* >> / YELLOW */ + { 0x0203, KEY_TEXT }, /* TELETEXT */ + { 0x0204, KEY_EPG }, /* EPG */ + { 0x0215, KEY_MENU }, /* MENU */ + + { 0x0303, KEY_CHANNELUP }, /* CH UP */ + { 0x0302, KEY_CHANNELDOWN }, /* CH DOWN */ + { 0x0301, KEY_FIRST }, /* |<< / GREEN */ + { 0x0300, KEY_LAST }, /* >>| / BLUE */ }; diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c index 7c596f92676..f4379c650a1 100644 --- a/drivers/media/dvb/dvb-usb/af9005-remote.c +++ b/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -35,43 +35,43 @@ MODULE_PARM_DESC(debug, struct dvb_usb_rc_key af9005_rc_keys[] = { - {0x01, 0xb7, KEY_POWER}, - {0x01, 0xa7, KEY_VOLUMEUP}, - {0x01, 0x87, KEY_CHANNELUP}, - {0x01, 0x7f, KEY_MUTE}, - {0x01, 0xbf, KEY_VOLUMEDOWN}, - {0x01, 0x3f, KEY_CHANNELDOWN}, - {0x01, 0xdf, KEY_1}, - {0x01, 0x5f, KEY_2}, - {0x01, 0x9f, KEY_3}, - {0x01, 0x1f, KEY_4}, - {0x01, 0xef, KEY_5}, - {0x01, 0x6f, KEY_6}, - {0x01, 0xaf, KEY_7}, - {0x01, 0x27, KEY_8}, - {0x01, 0x07, KEY_9}, - {0x01, 0xcf, KEY_ZOOM}, - {0x01, 0x4f, KEY_0}, - {0x01, 0x8f, KEY_GOTO}, /* marked jump on the remote */ + {0x01b7, KEY_POWER}, + {0x01a7, KEY_VOLUMEUP}, + {0x0187, KEY_CHANNELUP}, + {0x017f, KEY_MUTE}, + {0x01bf, KEY_VOLUMEDOWN}, + {0x013f, KEY_CHANNELDOWN}, + {0x01df, KEY_1}, + {0x015f, KEY_2}, + {0x019f, KEY_3}, + {0x011f, KEY_4}, + {0x01ef, KEY_5}, + {0x016f, KEY_6}, + {0x01af, KEY_7}, + {0x0127, KEY_8}, + {0x0107, KEY_9}, + {0x01cf, KEY_ZOOM}, + {0x014f, KEY_0}, + {0x018f, KEY_GOTO}, /* marked jump on the remote */ - {0x00, 0xbd, KEY_POWER}, - {0x00, 0x7d, KEY_VOLUMEUP}, - {0x00, 0xfd, KEY_CHANNELUP}, - {0x00, 0x9d, KEY_MUTE}, - {0x00, 0x5d, KEY_VOLUMEDOWN}, - {0x00, 0xdd, KEY_CHANNELDOWN}, - {0x00, 0xad, KEY_1}, - {0x00, 0x6d, KEY_2}, - {0x00, 0xed, KEY_3}, - {0x00, 0x8d, KEY_4}, - {0x00, 0x4d, KEY_5}, - {0x00, 0xcd, KEY_6}, - {0x00, 0xb5, KEY_7}, - {0x00, 0x75, KEY_8}, - {0x00, 0xf5, KEY_9}, - {0x00, 0x95, KEY_ZOOM}, - {0x00, 0x55, KEY_0}, - {0x00, 0xd5, KEY_GOTO}, /* marked jump on the remote */ + {0x00bd, KEY_POWER}, + {0x007d, KEY_VOLUMEUP}, + {0x00fd, KEY_CHANNELUP}, + {0x009d, KEY_MUTE}, + {0x005d, KEY_VOLUMEDOWN}, + {0x00dd, KEY_CHANNELDOWN}, + {0x00ad, KEY_1}, + {0x006d, KEY_2}, + {0x00ed, KEY_3}, + {0x008d, KEY_4}, + {0x004d, KEY_5}, + {0x00cd, KEY_6}, + {0x00b5, KEY_7}, + {0x0075, KEY_8}, + {0x00f5, KEY_9}, + {0x0095, KEY_ZOOM}, + {0x0055, KEY_0}, + {0x00d5, KEY_GOTO}, /* marked jump on the remote */ }; int af9005_rc_keys_size = ARRAY_SIZE(af9005_rc_keys); @@ -131,8 +131,8 @@ int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, return 0; } for (i = 0; i < af9005_rc_keys_size; i++) { - if (af9005_rc_keys[i].custom == cust - && af9005_rc_keys[i].data == dat) { + if (rc5_custom(&af9005_rc_keys[i]) == cust + && rc5_data(&af9005_rc_keys[i]) == dat) { *event = af9005_rc_keys[i].event; *state = REMOTE_KEY_PRESSED; deb_decode diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 4cb31e7c13c..cf042b309b4 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -61,10 +61,13 @@ static struct af9013_config af9015_af9013_config[] = { static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) { +#define BUF_LEN 63 +#define REQ_HDR_LEN 8 /* send header size */ +#define ACK_HDR_LEN 2 /* rece header size */ int act_len, ret; - u8 buf[64]; + u8 buf[BUF_LEN]; u8 write = 1; - u8 msg_len = 8; + u8 msg_len = REQ_HDR_LEN; static u8 seq; /* packet sequence number */ if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) @@ -81,7 +84,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) switch (req->cmd) { case GET_CONFIG: - case BOOT: case READ_MEMORY: case RECONNECT_USB: case GET_IR_CODE: @@ -95,11 +97,12 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) break; case WRITE_MEMORY: if (((req->addr & 0xff00) == 0xff00) || - ((req->addr & 0xae00) == 0xae00)) + ((req->addr & 0xff00) == 0xae00)) buf[0] = WRITE_VIRTUAL_MEMORY; case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: case DOWNLOAD_FIRMWARE: + case BOOT: break; default: err("unknown command:%d", req->cmd); @@ -107,17 +110,26 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) goto error_unlock; } + /* buffer overflow check */ + if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + err("too much data; cmd:%d len:%d", req->cmd, req->data_len); + ret = -EINVAL; + goto error_unlock; + } + /* write requested */ if (write) { - memcpy(&buf[8], req->data, req->data_len); + memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len); msg_len += req->data_len; } + deb_xfer(">>> "); debug_dump(buf, msg_len, deb_xfer); /* send req */ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, - &act_len, AF9015_USB_TIMEOUT); + &act_len, AF9015_USB_TIMEOUT); if (ret) err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); else @@ -130,10 +142,14 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) goto exit_unlock; - /* receive ack and data if read req */ - msg_len = 1 + 1 + req->data_len; /* seq + status + data len */ + /* write receives seq + status = 2 bytes + read receives seq + status + data = 2 + N bytes */ + msg_len = ACK_HDR_LEN; + if (!write) + msg_len += req->data_len; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, - &act_len, AF9015_USB_TIMEOUT); + &act_len, AF9015_USB_TIMEOUT); if (ret) { err("recv bulk message failed:%d", ret); ret = -1; @@ -159,7 +175,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) /* read request, copy returned data to return buf */ if (!write) - memcpy(req->data, &buf[2], req->data_len); + memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); error_unlock: exit_unlock: @@ -369,12 +385,14 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) u8 packet_size; deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. + We use smaller - about 1/4 from the original, 5 and 87. */ #define TS_PACKET_SIZE 188 -#define TS_USB20_PACKET_COUNT 348 +#define TS_USB20_PACKET_COUNT 87 #define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) -#define TS_USB11_PACKET_COUNT 21 +#define TS_USB11_PACKET_COUNT 5 #define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) #define TS_USB20_MAX_PACKET_SIZE 512 @@ -538,24 +556,22 @@ exit: /* dump eeprom */ static int af9015_eeprom_dump(struct dvb_usb_device *d) { - char buf[4+3*16+1], buf2[4]; u8 reg, val; for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - deb_info("%s\n", buf); - sprintf(buf, "%02x: ", reg); + deb_info(KERN_CONT "\n"); + deb_info(KERN_DEBUG "%02x:", reg); } if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0) - sprintf(buf2, "%02x ", val); + deb_info(KERN_CONT " %02x", val); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + deb_info(KERN_CONT " --"); if (reg == 0xff) break; } - deb_info("%s\n", buf); + deb_info(KERN_CONT "\n"); return 0; } @@ -870,13 +886,13 @@ static int af9015_read_config(struct usb_device *udev) /* USB1.1 set smaller buffersize and disable 2nd adapter */ if (udev->speed == USB_SPEED_FULL) { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB11_MAX_PACKET_SIZE; + = TS_USB11_FRAME_SIZE; /* disable 2nd adapter because we don't have PID-filters */ af9015_config.dual_mode = 0; } else { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB20_MAX_PACKET_SIZE; + = TS_USB20_FRAME_SIZE; } } @@ -1045,8 +1061,8 @@ static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; for (i = 0; i < d->props.rc_key_map_size; i++) { - if (!buf[1] && keymap[i].custom == buf[0] && - keymap[i].data == buf[2]) { + if (!buf[1] && rc5_custom(&keymap[i]) == buf[0] && + rc5_data(&keymap[i]) == buf[2]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; break; @@ -1266,6 +1282,7 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, +/* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1311,7 +1328,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1346,7 +1363,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { { .name = "KWorld PlusTV Dual DVB-T Stick " \ "(DVB-T 399U)", - .cold_ids = {&af9015_usb_table[4], NULL}, + .cold_ids = {&af9015_usb_table[4], + &af9015_usb_table[25], NULL}, .warm_ids = {NULL}, }, { @@ -1416,7 +1434,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1522,7 +1540,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 8d81a17c116..c41f30e4a1b 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -121,21 +121,21 @@ enum af9015_remote { /* Leadtek WinFast DTV Dongle Gold */ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x00, 0x27, KEY_0 }, - { 0x00, 0x28, KEY_ENTER }, - { 0x00, 0x4f, KEY_VOLUMEUP }, - { 0x00, 0x50, KEY_VOLUMEDOWN }, - { 0x00, 0x51, KEY_CHANNELDOWN }, - { 0x00, 0x52, KEY_CHANNELUP }, + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0027, KEY_0 }, + { 0x0028, KEY_ENTER }, + { 0x004f, KEY_VOLUMEUP }, + { 0x0050, KEY_VOLUMEDOWN }, + { 0x0051, KEY_CHANNELDOWN }, + { 0x0052, KEY_CHANNELUP }, }; static u8 af9015_ir_table_leadtek[] = { @@ -193,60 +193,60 @@ static u8 af9015_ir_table_leadtek[] = { /* TwinHan AzureWave AD-TU700(704J) */ static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = { - { 0x05, 0x3f, KEY_POWER }, - { 0x00, 0x19, KEY_FAVORITES }, /* Favorite List */ - { 0x00, 0x04, KEY_TEXT }, /* Teletext */ - { 0x00, 0x0e, KEY_POWER }, - { 0x00, 0x0e, KEY_INFO }, /* Preview */ - { 0x00, 0x08, KEY_EPG }, /* Info/EPG */ - { 0x00, 0x0f, KEY_LIST }, /* Record List */ - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x00, 0x27, KEY_0 }, - { 0x00, 0x29, KEY_CANCEL }, /* Cancel */ - { 0x00, 0x4c, KEY_CLEAR }, /* Clear */ - { 0x00, 0x2a, KEY_BACK }, /* Back */ - { 0x00, 0x2b, KEY_TAB }, /* Tab */ - { 0x00, 0x52, KEY_UP }, /* up arrow */ - { 0x00, 0x51, KEY_DOWN }, /* down arrow */ - { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ - { 0x00, 0x50, KEY_LEFT }, /* left arrow */ - { 0x00, 0x28, KEY_ENTER }, /* Enter / ok */ - { 0x02, 0x52, KEY_VOLUMEUP }, - { 0x02, 0x51, KEY_VOLUMEDOWN }, - { 0x00, 0x4e, KEY_CHANNELDOWN }, - { 0x00, 0x4b, KEY_CHANNELUP }, - { 0x00, 0x4a, KEY_RECORD }, - { 0x01, 0x11, KEY_PLAY }, - { 0x00, 0x17, KEY_PAUSE }, - { 0x00, 0x0c, KEY_REWIND }, /* FR << */ - { 0x00, 0x11, KEY_FASTFORWARD }, /* FF >> */ - { 0x01, 0x15, KEY_PREVIOUS }, /* Replay */ - { 0x01, 0x0e, KEY_NEXT }, /* Skip */ - { 0x00, 0x13, KEY_CAMERA }, /* Capture */ - { 0x01, 0x0f, KEY_LANGUAGE }, /* SAP */ - { 0x01, 0x13, KEY_TV2 }, /* PIP */ - { 0x00, 0x1d, KEY_ZOOM }, /* Full Screen */ - { 0x01, 0x17, KEY_SUBTITLE }, /* Subtitle / CC */ - { 0x00, 0x10, KEY_MUTE }, - { 0x01, 0x19, KEY_AUDIO }, /* L/R */ /* TODO better event */ - { 0x01, 0x16, KEY_SLEEP }, /* Hibernate */ - { 0x01, 0x16, KEY_SWITCHVIDEOMODE }, + { 0x053f, KEY_POWER }, + { 0x0019, KEY_FAVORITES }, /* Favorite List */ + { 0x0004, KEY_TEXT }, /* Teletext */ + { 0x000e, KEY_POWER }, + { 0x000e, KEY_INFO }, /* Preview */ + { 0x0008, KEY_EPG }, /* Info/EPG */ + { 0x000f, KEY_LIST }, /* Record List */ + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0027, KEY_0 }, + { 0x0029, KEY_CANCEL }, /* Cancel */ + { 0x004c, KEY_CLEAR }, /* Clear */ + { 0x002a, KEY_BACK }, /* Back */ + { 0x002b, KEY_TAB }, /* Tab */ + { 0x0052, KEY_UP }, /* up arrow */ + { 0x0051, KEY_DOWN }, /* down arrow */ + { 0x004f, KEY_RIGHT }, /* right arrow */ + { 0x0050, KEY_LEFT }, /* left arrow */ + { 0x0028, KEY_ENTER }, /* Enter / ok */ + { 0x0252, KEY_VOLUMEUP }, + { 0x0251, KEY_VOLUMEDOWN }, + { 0x004e, KEY_CHANNELDOWN }, + { 0x004b, KEY_CHANNELUP }, + { 0x004a, KEY_RECORD }, + { 0x0111, KEY_PLAY }, + { 0x0017, KEY_PAUSE }, + { 0x000c, KEY_REWIND }, /* FR << */ + { 0x0011, KEY_FASTFORWARD }, /* FF >> */ + { 0x0115, KEY_PREVIOUS }, /* Replay */ + { 0x010e, KEY_NEXT }, /* Skip */ + { 0x0013, KEY_CAMERA }, /* Capture */ + { 0x010f, KEY_LANGUAGE }, /* SAP */ + { 0x0113, KEY_TV2 }, /* PIP */ + { 0x001d, KEY_ZOOM }, /* Full Screen */ + { 0x0117, KEY_SUBTITLE }, /* Subtitle / CC */ + { 0x0010, KEY_MUTE }, + { 0x0119, KEY_AUDIO }, /* L/R */ /* TODO better event */ + { 0x0116, KEY_SLEEP }, /* Hibernate */ + { 0x0116, KEY_SWITCHVIDEOMODE }, /* A/V */ /* TODO does not work */ - { 0x00, 0x06, KEY_AGAIN }, /* Recall */ - { 0x01, 0x16, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */ - { 0x01, 0x16, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */ - { 0x02, 0x15, KEY_RED }, - { 0x02, 0x0a, KEY_GREEN }, - { 0x02, 0x1c, KEY_YELLOW }, - { 0x02, 0x05, KEY_BLUE }, + { 0x0006, KEY_AGAIN }, /* Recall */ + { 0x0116, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */ + { 0x0116, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */ + { 0x0215, KEY_RED }, + { 0x020a, KEY_GREEN }, + { 0x021c, KEY_YELLOW }, + { 0x0205, KEY_BLUE }, }; static u8 af9015_ir_table_twinhan[] = { @@ -304,24 +304,24 @@ static u8 af9015_ir_table_twinhan[] = { /* A-Link DTU(m) */ static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = { - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x00, 0x27, KEY_0 }, - { 0x00, 0x2e, KEY_CHANNELUP }, - { 0x00, 0x2d, KEY_CHANNELDOWN }, - { 0x04, 0x28, KEY_ZOOM }, - { 0x00, 0x41, KEY_MUTE }, - { 0x00, 0x42, KEY_VOLUMEDOWN }, - { 0x00, 0x43, KEY_VOLUMEUP }, - { 0x00, 0x44, KEY_GOTO }, /* jump */ - { 0x05, 0x45, KEY_POWER }, + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0027, KEY_0 }, + { 0x002e, KEY_CHANNELUP }, + { 0x002d, KEY_CHANNELDOWN }, + { 0x0428, KEY_ZOOM }, + { 0x0041, KEY_MUTE }, + { 0x0042, KEY_VOLUMEDOWN }, + { 0x0043, KEY_VOLUMEUP }, + { 0x0044, KEY_GOTO }, /* jump */ + { 0x0545, KEY_POWER }, }; static u8 af9015_ir_table_a_link[] = { @@ -347,24 +347,24 @@ static u8 af9015_ir_table_a_link[] = { /* MSI DIGIVOX mini II V3.0 */ static struct dvb_usb_rc_key af9015_rc_keys_msi[] = { - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x00, 0x27, KEY_0 }, - { 0x03, 0x0f, KEY_CHANNELUP }, - { 0x03, 0x0e, KEY_CHANNELDOWN }, - { 0x00, 0x42, KEY_VOLUMEDOWN }, - { 0x00, 0x43, KEY_VOLUMEUP }, - { 0x05, 0x45, KEY_POWER }, - { 0x00, 0x52, KEY_UP }, /* up */ - { 0x00, 0x51, KEY_DOWN }, /* down */ - { 0x00, 0x28, KEY_ENTER }, + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0027, KEY_0 }, + { 0x030f, KEY_CHANNELUP }, + { 0x030e, KEY_CHANNELDOWN }, + { 0x0042, KEY_VOLUMEDOWN }, + { 0x0043, KEY_VOLUMEUP }, + { 0x0545, KEY_POWER }, + { 0x0052, KEY_UP }, /* up */ + { 0x0051, KEY_DOWN }, /* down */ + { 0x0028, KEY_ENTER }, }; static u8 af9015_ir_table_msi[] = { @@ -390,42 +390,42 @@ static u8 af9015_ir_table_msi[] = { /* MYGICTV U718 */ static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = { - { 0x00, 0x3d, KEY_SWITCHVIDEOMODE }, + { 0x003d, KEY_SWITCHVIDEOMODE }, /* TV / AV */ - { 0x05, 0x45, KEY_POWER }, - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x00, 0x27, KEY_0 }, - { 0x00, 0x41, KEY_MUTE }, - { 0x00, 0x2a, KEY_ESC }, /* Esc */ - { 0x00, 0x2e, KEY_CHANNELUP }, - { 0x00, 0x2d, KEY_CHANNELDOWN }, - { 0x00, 0x42, KEY_VOLUMEDOWN }, - { 0x00, 0x43, KEY_VOLUMEUP }, - { 0x00, 0x52, KEY_UP }, /* up arrow */ - { 0x00, 0x51, KEY_DOWN }, /* down arrow */ - { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ - { 0x00, 0x50, KEY_LEFT }, /* left arrow */ - { 0x00, 0x28, KEY_ENTER }, /* ok */ - { 0x01, 0x15, KEY_RECORD }, - { 0x03, 0x13, KEY_PLAY }, - { 0x01, 0x13, KEY_PAUSE }, - { 0x01, 0x16, KEY_STOP }, - { 0x03, 0x07, KEY_REWIND }, /* FR << */ - { 0x03, 0x09, KEY_FASTFORWARD }, /* FF >> */ - { 0x00, 0x3b, KEY_TIME }, /* TimeShift */ - { 0x00, 0x3e, KEY_CAMERA }, /* Snapshot */ - { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */ - { 0x00, 0x00, KEY_ZOOM }, /* 'select' (?) */ - { 0x03, 0x16, KEY_SHUFFLE }, /* Shuffle */ - { 0x03, 0x45, KEY_POWER }, + { 0x0545, KEY_POWER }, + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0027, KEY_0 }, + { 0x0041, KEY_MUTE }, + { 0x002a, KEY_ESC }, /* Esc */ + { 0x002e, KEY_CHANNELUP }, + { 0x002d, KEY_CHANNELDOWN }, + { 0x0042, KEY_VOLUMEDOWN }, + { 0x0043, KEY_VOLUMEUP }, + { 0x0052, KEY_UP }, /* up arrow */ + { 0x0051, KEY_DOWN }, /* down arrow */ + { 0x004f, KEY_RIGHT }, /* right arrow */ + { 0x0050, KEY_LEFT }, /* left arrow */ + { 0x0028, KEY_ENTER }, /* ok */ + { 0x0115, KEY_RECORD }, + { 0x0313, KEY_PLAY }, + { 0x0113, KEY_PAUSE }, + { 0x0116, KEY_STOP }, + { 0x0307, KEY_REWIND }, /* FR << */ + { 0x0309, KEY_FASTFORWARD }, /* FF >> */ + { 0x003b, KEY_TIME }, /* TimeShift */ + { 0x003e, KEY_CAMERA }, /* Snapshot */ + { 0x0316, KEY_CYCLEWINDOWS }, /* yellow, min / max */ + { 0x0000, KEY_ZOOM }, /* 'select' (?) */ + { 0x0316, KEY_SHUFFLE }, /* Shuffle */ + { 0x0345, KEY_POWER }, }; static u8 af9015_ir_table_mygictv[] = { @@ -516,41 +516,41 @@ static u8 af9015_ir_table_kworld[] = { /* AverMedia Volar X */ static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = { - { 0x05, 0x3d, KEY_PROG1 }, /* SOURCE */ - { 0x05, 0x12, KEY_POWER }, /* POWER */ - { 0x05, 0x1e, KEY_1 }, /* 1 */ - { 0x05, 0x1f, KEY_2 }, /* 2 */ - { 0x05, 0x20, KEY_3 }, /* 3 */ - { 0x05, 0x21, KEY_4 }, /* 4 */ - { 0x05, 0x22, KEY_5 }, /* 5 */ - { 0x05, 0x23, KEY_6 }, /* 6 */ - { 0x05, 0x24, KEY_7 }, /* 7 */ - { 0x05, 0x25, KEY_8 }, /* 8 */ - { 0x05, 0x26, KEY_9 }, /* 9 */ - { 0x05, 0x3f, KEY_LEFT }, /* L / DISPLAY */ - { 0x05, 0x27, KEY_0 }, /* 0 */ - { 0x05, 0x0f, KEY_RIGHT }, /* R / CH RTN */ - { 0x05, 0x18, KEY_PROG2 }, /* SNAP SHOT */ - { 0x05, 0x1c, KEY_PROG3 }, /* 16-CH PREV */ - { 0x05, 0x2d, KEY_VOLUMEDOWN }, /* VOL DOWN */ - { 0x05, 0x3e, KEY_ZOOM }, /* FULL SCREEN */ - { 0x05, 0x2e, KEY_VOLUMEUP }, /* VOL UP */ - { 0x05, 0x10, KEY_MUTE }, /* MUTE */ - { 0x05, 0x04, KEY_AUDIO }, /* AUDIO */ - { 0x05, 0x15, KEY_RECORD }, /* RECORD */ - { 0x05, 0x11, KEY_PLAY }, /* PLAY */ - { 0x05, 0x16, KEY_STOP }, /* STOP */ - { 0x05, 0x0c, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ - { 0x05, 0x05, KEY_BACK }, /* << / RED */ - { 0x05, 0x09, KEY_FORWARD }, /* >> / YELLOW */ - { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */ - { 0x05, 0x0a, KEY_EPG }, /* EPG */ - { 0x05, 0x13, KEY_MENU }, /* MENU */ - - { 0x05, 0x0e, KEY_CHANNELUP }, /* CH UP */ - { 0x05, 0x0d, KEY_CHANNELDOWN }, /* CH DOWN */ - { 0x05, 0x19, KEY_FIRST }, /* |<< / GREEN */ - { 0x05, 0x08, KEY_LAST }, /* >>| / BLUE */ + { 0x053d, KEY_PROG1 }, /* SOURCE */ + { 0x0512, KEY_POWER }, /* POWER */ + { 0x051e, KEY_1 }, /* 1 */ + { 0x051f, KEY_2 }, /* 2 */ + { 0x0520, KEY_3 }, /* 3 */ + { 0x0521, KEY_4 }, /* 4 */ + { 0x0522, KEY_5 }, /* 5 */ + { 0x0523, KEY_6 }, /* 6 */ + { 0x0524, KEY_7 }, /* 7 */ + { 0x0525, KEY_8 }, /* 8 */ + { 0x0526, KEY_9 }, /* 9 */ + { 0x053f, KEY_LEFT }, /* L / DISPLAY */ + { 0x0527, KEY_0 }, /* 0 */ + { 0x050f, KEY_RIGHT }, /* R / CH RTN */ + { 0x0518, KEY_PROG2 }, /* SNAP SHOT */ + { 0x051c, KEY_PROG3 }, /* 16-CH PREV */ + { 0x052d, KEY_VOLUMEDOWN }, /* VOL DOWN */ + { 0x053e, KEY_ZOOM }, /* FULL SCREEN */ + { 0x052e, KEY_VOLUMEUP }, /* VOL UP */ + { 0x0510, KEY_MUTE }, /* MUTE */ + { 0x0504, KEY_AUDIO }, /* AUDIO */ + { 0x0515, KEY_RECORD }, /* RECORD */ + { 0x0511, KEY_PLAY }, /* PLAY */ + { 0x0516, KEY_STOP }, /* STOP */ + { 0x050c, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ + { 0x0505, KEY_BACK }, /* << / RED */ + { 0x0509, KEY_FORWARD }, /* >> / YELLOW */ + { 0x0517, KEY_TEXT }, /* TELETEXT */ + { 0x050a, KEY_EPG }, /* EPG */ + { 0x0513, KEY_MENU }, /* MENU */ + + { 0x050e, KEY_CHANNELUP }, /* CH UP */ + { 0x050d, KEY_CHANNELDOWN }, /* CH DOWN */ + { 0x0519, KEY_FIRST }, /* |<< / GREEN */ + { 0x0508, KEY_LAST }, /* >>| / BLUE */ }; static u8 af9015_ir_table_avermedia[] = { @@ -622,34 +622,34 @@ static u8 af9015_ir_table_avermedia_ks[] = { /* Digittrade DVB-T USB Stick */ static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = { - { 0x01, 0x0f, KEY_LAST }, /* RETURN */ - { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */ - { 0x01, 0x08, KEY_EPG }, /* EPG */ - { 0x05, 0x13, KEY_POWER }, /* POWER */ - { 0x01, 0x09, KEY_ZOOM }, /* FULLSCREEN */ - { 0x00, 0x40, KEY_AUDIO }, /* DUAL SOUND */ - { 0x00, 0x2c, KEY_PRINT }, /* SNAPSHOT */ - { 0x05, 0x16, KEY_SUBTITLE }, /* SUBTITLE */ - { 0x00, 0x52, KEY_CHANNELUP }, /* CH Up */ - { 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */ - { 0x00, 0x57, KEY_VOLUMEUP }, /* Vol Up */ - { 0x00, 0x56, KEY_VOLUMEDOWN }, /* Vol Dn */ - { 0x01, 0x10, KEY_MUTE }, /* MUTE */ - { 0x00, 0x27, KEY_0 }, - { 0x00, 0x1e, KEY_1 }, - { 0x00, 0x1f, KEY_2 }, - { 0x00, 0x20, KEY_3 }, - { 0x00, 0x21, KEY_4 }, - { 0x00, 0x22, KEY_5 }, - { 0x00, 0x23, KEY_6 }, - { 0x00, 0x24, KEY_7 }, - { 0x00, 0x25, KEY_8 }, - { 0x00, 0x26, KEY_9 }, - { 0x01, 0x17, KEY_PLAYPAUSE }, /* TIMESHIFT */ - { 0x01, 0x15, KEY_RECORD }, /* RECORD */ - { 0x03, 0x13, KEY_PLAY }, /* PLAY */ - { 0x01, 0x16, KEY_STOP }, /* STOP */ - { 0x01, 0x13, KEY_PAUSE }, /* PAUSE */ + { 0x010f, KEY_LAST }, /* RETURN */ + { 0x0517, KEY_TEXT }, /* TELETEXT */ + { 0x0108, KEY_EPG }, /* EPG */ + { 0x0513, KEY_POWER }, /* POWER */ + { 0x0109, KEY_ZOOM }, /* FULLSCREEN */ + { 0x0040, KEY_AUDIO }, /* DUAL SOUND */ + { 0x002c, KEY_PRINT }, /* SNAPSHOT */ + { 0x0516, KEY_SUBTITLE }, /* SUBTITLE */ + { 0x0052, KEY_CHANNELUP }, /* CH Up */ + { 0x0051, KEY_CHANNELDOWN },/* Ch Dn */ + { 0x0057, KEY_VOLUMEUP }, /* Vol Up */ + { 0x0056, KEY_VOLUMEDOWN }, /* Vol Dn */ + { 0x0110, KEY_MUTE }, /* MUTE */ + { 0x0027, KEY_0 }, + { 0x001e, KEY_1 }, + { 0x001f, KEY_2 }, + { 0x0020, KEY_3 }, + { 0x0021, KEY_4 }, + { 0x0022, KEY_5 }, + { 0x0023, KEY_6 }, + { 0x0024, KEY_7 }, + { 0x0025, KEY_8 }, + { 0x0026, KEY_9 }, + { 0x0117, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x0115, KEY_RECORD }, /* RECORD */ + { 0x0313, KEY_PLAY }, /* PLAY */ + { 0x0116, KEY_STOP }, /* STOP */ + { 0x0113, KEY_PAUSE }, /* PAUSE */ }; static u8 af9015_ir_table_digittrade[] = { @@ -685,34 +685,34 @@ static u8 af9015_ir_table_digittrade[] = { /* TREKSTOR DVB-T USB Stick */ static struct dvb_usb_rc_key af9015_rc_keys_trekstor[] = { - { 0x07, 0x04, KEY_AGAIN }, /* Home */ - { 0x07, 0x05, KEY_MUTE }, /* Mute */ - { 0x07, 0x06, KEY_UP }, /* Up */ - { 0x07, 0x07, KEY_DOWN }, /* Down */ - { 0x07, 0x09, KEY_RIGHT }, /* Right */ - { 0x07, 0x0a, KEY_ENTER }, /* OK */ - { 0x07, 0x0b, KEY_FASTFORWARD }, /* Fast forward */ - { 0x07, 0x0c, KEY_REWIND }, /* Rewind */ - { 0x07, 0x0d, KEY_PLAY }, /* Play/Pause */ - { 0x07, 0x0e, KEY_VOLUMEUP }, /* Volume + */ - { 0x07, 0x0f, KEY_VOLUMEDOWN }, /* Volume - */ - { 0x07, 0x10, KEY_RECORD }, /* Record */ - { 0x07, 0x11, KEY_STOP }, /* Stop */ - { 0x07, 0x12, KEY_ZOOM }, /* TV */ - { 0x07, 0x13, KEY_EPG }, /* Info/EPG */ - { 0x07, 0x14, KEY_CHANNELDOWN }, /* Channel - */ - { 0x07, 0x15, KEY_CHANNELUP }, /* Channel + */ - { 0x07, 0x1e, KEY_1 }, - { 0x07, 0x1f, KEY_2 }, - { 0x07, 0x20, KEY_3 }, - { 0x07, 0x21, KEY_4 }, - { 0x07, 0x22, KEY_5 }, - { 0x07, 0x23, KEY_6 }, - { 0x07, 0x24, KEY_7 }, - { 0x07, 0x25, KEY_8 }, - { 0x07, 0x26, KEY_9 }, - { 0x07, 0x08, KEY_LEFT }, /* LEFT */ - { 0x07, 0x27, KEY_0 }, + { 0x0704, KEY_AGAIN }, /* Home */ + { 0x0705, KEY_MUTE }, /* Mute */ + { 0x0706, KEY_UP }, /* Up */ + { 0x0707, KEY_DOWN }, /* Down */ + { 0x0709, KEY_RIGHT }, /* Right */ + { 0x070a, KEY_ENTER }, /* OK */ + { 0x070b, KEY_FASTFORWARD }, /* Fast forward */ + { 0x070c, KEY_REWIND }, /* Rewind */ + { 0x070d, KEY_PLAY }, /* Play/Pause */ + { 0x070e, KEY_VOLUMEUP }, /* Volume + */ + { 0x070f, KEY_VOLUMEDOWN }, /* Volume - */ + { 0x0710, KEY_RECORD }, /* Record */ + { 0x0711, KEY_STOP }, /* Stop */ + { 0x0712, KEY_ZOOM }, /* TV */ + { 0x0713, KEY_EPG }, /* Info/EPG */ + { 0x0714, KEY_CHANNELDOWN }, /* Channel - */ + { 0x0715, KEY_CHANNELUP }, /* Channel + */ + { 0x071e, KEY_1 }, + { 0x071f, KEY_2 }, + { 0x0720, KEY_3 }, + { 0x0721, KEY_4 }, + { 0x0722, KEY_5 }, + { 0x0723, KEY_6 }, + { 0x0724, KEY_7 }, + { 0x0725, KEY_8 }, + { 0x0726, KEY_9 }, + { 0x0708, KEY_LEFT }, /* LEFT */ + { 0x0727, KEY_0 }, }; static u8 af9015_ir_table_trekstor[] = { diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index c6e7b4215d6..2ae7f648eff 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -203,11 +203,11 @@ static struct i2c_algorithm anysee_i2c_algo = { static int anysee_mt352_demod_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -389,8 +389,8 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == ircode[0] && - keymap[i].data == ircode[1]) { + if (rc5_custom(&keymap[i]) == ircode[0] && + rc5_data(&keymap[i]) == ircode[1]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; return 0; @@ -400,50 +400,50 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } static struct dvb_usb_rc_key anysee_rc_keys[] = { - { 0x01, 0x00, KEY_0 }, - { 0x01, 0x01, KEY_1 }, - { 0x01, 0x02, KEY_2 }, - { 0x01, 0x03, KEY_3 }, - { 0x01, 0x04, KEY_4 }, - { 0x01, 0x05, KEY_5 }, - { 0x01, 0x06, KEY_6 }, - { 0x01, 0x07, KEY_7 }, - { 0x01, 0x08, KEY_8 }, - { 0x01, 0x09, KEY_9 }, - { 0x01, 0x0a, KEY_POWER }, - { 0x01, 0x0b, KEY_DOCUMENTS }, /* * */ - { 0x01, 0x19, KEY_FAVORITES }, - { 0x01, 0x20, KEY_SLEEP }, - { 0x01, 0x21, KEY_MODE }, /* 4:3 / 16:9 select */ - { 0x01, 0x22, KEY_ZOOM }, - { 0x01, 0x47, KEY_TEXT }, - { 0x01, 0x16, KEY_TV }, /* TV / radio select */ - { 0x01, 0x1e, KEY_LANGUAGE }, /* Second Audio Program */ - { 0x01, 0x1a, KEY_SUBTITLE }, - { 0x01, 0x1b, KEY_CAMERA }, /* screenshot */ - { 0x01, 0x42, KEY_MUTE }, - { 0x01, 0x0e, KEY_MENU }, - { 0x01, 0x0f, KEY_EPG }, - { 0x01, 0x17, KEY_INFO }, - { 0x01, 0x10, KEY_EXIT }, - { 0x01, 0x13, KEY_VOLUMEUP }, - { 0x01, 0x12, KEY_VOLUMEDOWN }, - { 0x01, 0x11, KEY_CHANNELUP }, - { 0x01, 0x14, KEY_CHANNELDOWN }, - { 0x01, 0x15, KEY_OK }, - { 0x01, 0x1d, KEY_RED }, - { 0x01, 0x1f, KEY_GREEN }, - { 0x01, 0x1c, KEY_YELLOW }, - { 0x01, 0x44, KEY_BLUE }, - { 0x01, 0x0c, KEY_SHUFFLE }, /* snapshot */ - { 0x01, 0x48, KEY_STOP }, - { 0x01, 0x50, KEY_PLAY }, - { 0x01, 0x51, KEY_PAUSE }, - { 0x01, 0x49, KEY_RECORD }, - { 0x01, 0x18, KEY_PREVIOUS }, /* |<< */ - { 0x01, 0x0d, KEY_NEXT }, /* >>| */ - { 0x01, 0x24, KEY_PROG1 }, /* F1 */ - { 0x01, 0x25, KEY_PROG2 }, /* F2 */ + { 0x0100, KEY_0 }, + { 0x0101, KEY_1 }, + { 0x0102, KEY_2 }, + { 0x0103, KEY_3 }, + { 0x0104, KEY_4 }, + { 0x0105, KEY_5 }, + { 0x0106, KEY_6 }, + { 0x0107, KEY_7 }, + { 0x0108, KEY_8 }, + { 0x0109, KEY_9 }, + { 0x010a, KEY_POWER }, + { 0x010b, KEY_DOCUMENTS }, /* * */ + { 0x0119, KEY_FAVORITES }, + { 0x0120, KEY_SLEEP }, + { 0x0121, KEY_MODE }, /* 4:3 / 16:9 select */ + { 0x0122, KEY_ZOOM }, + { 0x0147, KEY_TEXT }, + { 0x0116, KEY_TV }, /* TV / radio select */ + { 0x011e, KEY_LANGUAGE }, /* Second Audio Program */ + { 0x011a, KEY_SUBTITLE }, + { 0x011b, KEY_CAMERA }, /* screenshot */ + { 0x0142, KEY_MUTE }, + { 0x010e, KEY_MENU }, + { 0x010f, KEY_EPG }, + { 0x0117, KEY_INFO }, + { 0x0110, KEY_EXIT }, + { 0x0113, KEY_VOLUMEUP }, + { 0x0112, KEY_VOLUMEDOWN }, + { 0x0111, KEY_CHANNELUP }, + { 0x0114, KEY_CHANNELDOWN }, + { 0x0115, KEY_OK }, + { 0x011d, KEY_RED }, + { 0x011f, KEY_GREEN }, + { 0x011c, KEY_YELLOW }, + { 0x0144, KEY_BLUE }, + { 0x010c, KEY_SHUFFLE }, /* snapshot */ + { 0x0148, KEY_STOP }, + { 0x0150, KEY_PLAY }, + { 0x0151, KEY_PAUSE }, + { 0x0149, KEY_RECORD }, + { 0x0118, KEY_PREVIOUS }, /* |<< */ + { 0x010d, KEY_NEXT }, /* >>| */ + { 0x0124, KEY_PROG1 }, /* F1 */ + { 0x0125, KEY_PROG2 }, /* F2 */ }; /* DVB USB Driver stuff */ @@ -485,7 +485,7 @@ static int anysee_probe(struct usb_interface *intf, return ret; } -static struct usb_device_id anysee_table [] = { +static struct usb_device_id anysee_table[] = { { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) }, { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) }, { } /* Terminating entry */ @@ -511,7 +511,7 @@ static struct dvb_usb_device_properties anysee_properties = { .endpoint = 0x82, .u = { .bulk = { - .buffersize = 512, + .buffersize = (16*512), } } }, diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c index 52badc00e67..0737c637789 100644 --- a/drivers/media/dvb/dvb-usb/ce6230.c +++ b/drivers/media/dvb/dvb-usb/ce6230.c @@ -274,7 +274,7 @@ static struct dvb_usb_device_properties ce6230_properties = { .endpoint = 0x82, .u = { .bulk = { - .buffersize = 512, + .buffersize = (16*512), } } }, diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c index 80e37a0d089..e37ac4d4860 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -85,43 +85,43 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) } static struct dvb_usb_rc_key cinergyt2_rc_keys[] = { - { 0x04, 0x01, KEY_POWER }, - { 0x04, 0x02, KEY_1 }, - { 0x04, 0x03, KEY_2 }, - { 0x04, 0x04, KEY_3 }, - { 0x04, 0x05, KEY_4 }, - { 0x04, 0x06, KEY_5 }, - { 0x04, 0x07, KEY_6 }, - { 0x04, 0x08, KEY_7 }, - { 0x04, 0x09, KEY_8 }, - { 0x04, 0x0a, KEY_9 }, - { 0x04, 0x0c, KEY_0 }, - { 0x04, 0x0b, KEY_VIDEO }, - { 0x04, 0x0d, KEY_REFRESH }, - { 0x04, 0x0e, KEY_SELECT }, - { 0x04, 0x0f, KEY_EPG }, - { 0x04, 0x10, KEY_UP }, - { 0x04, 0x14, KEY_DOWN }, - { 0x04, 0x11, KEY_LEFT }, - { 0x04, 0x13, KEY_RIGHT }, - { 0x04, 0x12, KEY_OK }, - { 0x04, 0x15, KEY_TEXT }, - { 0x04, 0x16, KEY_INFO }, - { 0x04, 0x17, KEY_RED }, - { 0x04, 0x18, KEY_GREEN }, - { 0x04, 0x19, KEY_YELLOW }, - { 0x04, 0x1a, KEY_BLUE }, - { 0x04, 0x1c, KEY_VOLUMEUP }, - { 0x04, 0x1e, KEY_VOLUMEDOWN }, - { 0x04, 0x1d, KEY_MUTE }, - { 0x04, 0x1b, KEY_CHANNELUP }, - { 0x04, 0x1f, KEY_CHANNELDOWN }, - { 0x04, 0x40, KEY_PAUSE }, - { 0x04, 0x4c, KEY_PLAY }, - { 0x04, 0x58, KEY_RECORD }, - { 0x04, 0x54, KEY_PREVIOUS }, - { 0x04, 0x48, KEY_STOP }, - { 0x04, 0x5c, KEY_NEXT } + { 0x0401, KEY_POWER }, + { 0x0402, KEY_1 }, + { 0x0403, KEY_2 }, + { 0x0404, KEY_3 }, + { 0x0405, KEY_4 }, + { 0x0406, KEY_5 }, + { 0x0407, KEY_6 }, + { 0x0408, KEY_7 }, + { 0x0409, KEY_8 }, + { 0x040a, KEY_9 }, + { 0x040c, KEY_0 }, + { 0x040b, KEY_VIDEO }, + { 0x040d, KEY_REFRESH }, + { 0x040e, KEY_SELECT }, + { 0x040f, KEY_EPG }, + { 0x0410, KEY_UP }, + { 0x0414, KEY_DOWN }, + { 0x0411, KEY_LEFT }, + { 0x0413, KEY_RIGHT }, + { 0x0412, KEY_OK }, + { 0x0415, KEY_TEXT }, + { 0x0416, KEY_INFO }, + { 0x0417, KEY_RED }, + { 0x0418, KEY_GREEN }, + { 0x0419, KEY_YELLOW }, + { 0x041a, KEY_BLUE }, + { 0x041c, KEY_VOLUMEUP }, + { 0x041e, KEY_VOLUMEDOWN }, + { 0x041d, KEY_MUTE }, + { 0x041b, KEY_CHANNELUP }, + { 0x041f, KEY_CHANNELDOWN }, + { 0x0440, KEY_PAUSE }, + { 0x044c, KEY_PLAY }, + { 0x0458, KEY_RECORD }, + { 0x0454, KEY_PREVIOUS }, + { 0x0448, KEY_STOP }, + { 0x045c, KEY_NEXT } }; /* Number of keypresses to ignore before detect repeating */ diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c index 649f25cca49..9cd51ac1207 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c @@ -275,6 +275,7 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe, param.tps = cpu_to_le16(compute_tps(fep)); param.freq = cpu_to_le32(fep->frequency / 1000); param.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ; + param.flags = 0; err = dvb_usb_generic_rw(state->d, (char *)¶m, sizeof(param), diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 406d7fba369..f65591fb7ce 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -38,7 +38,7 @@ #include "mxl5005s.h" #include "dib7000p.h" #include "dib0070.h" -#include "lgs8gl5.h" +#include "lgs8gxx.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -392,8 +392,8 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == ircode[2] && - keymap[i].data == ircode[3]) { + if (rc5_custom(&keymap[i]) == ircode[2] && + rc5_data(&keymap[i]) == ircode[3]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; @@ -420,8 +420,8 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, return 0; for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == ircode[1] && - keymap[i].data == ircode[2]) { + if (rc5_custom(&keymap[i]) == ircode[1] && + rc5_data(&keymap[i]) == ircode[2]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; @@ -446,8 +446,8 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, return 0; for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == ircode[0] && - keymap[i].data == ircode[1]) { + if (rc5_custom(&keymap[i]) == ircode[0] && + rc5_data(&keymap[i]) == ircode[1]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; @@ -459,128 +459,128 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, } static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { - { 0xfe, 0x02, KEY_TV }, - { 0xfe, 0x0e, KEY_MP3 }, - { 0xfe, 0x1a, KEY_DVD }, - { 0xfe, 0x1e, KEY_FAVORITES }, - { 0xfe, 0x16, KEY_SETUP }, - { 0xfe, 0x46, KEY_POWER2 }, - { 0xfe, 0x0a, KEY_EPG }, - { 0xfe, 0x49, KEY_BACK }, - { 0xfe, 0x4d, KEY_MENU }, - { 0xfe, 0x51, KEY_UP }, - { 0xfe, 0x5b, KEY_LEFT }, - { 0xfe, 0x5f, KEY_RIGHT }, - { 0xfe, 0x53, KEY_DOWN }, - { 0xfe, 0x5e, KEY_OK }, - { 0xfe, 0x59, KEY_INFO }, - { 0xfe, 0x55, KEY_TAB }, - { 0xfe, 0x0f, KEY_PREVIOUSSONG },/* Replay */ - { 0xfe, 0x12, KEY_NEXTSONG }, /* Skip */ - { 0xfe, 0x42, KEY_ENTER }, /* Windows/Start */ - { 0xfe, 0x15, KEY_VOLUMEUP }, - { 0xfe, 0x05, KEY_VOLUMEDOWN }, - { 0xfe, 0x11, KEY_CHANNELUP }, - { 0xfe, 0x09, KEY_CHANNELDOWN }, - { 0xfe, 0x52, KEY_CAMERA }, - { 0xfe, 0x5a, KEY_TUNER }, /* Live */ - { 0xfe, 0x19, KEY_OPEN }, - { 0xfe, 0x0b, KEY_1 }, - { 0xfe, 0x17, KEY_2 }, - { 0xfe, 0x1b, KEY_3 }, - { 0xfe, 0x07, KEY_4 }, - { 0xfe, 0x50, KEY_5 }, - { 0xfe, 0x54, KEY_6 }, - { 0xfe, 0x48, KEY_7 }, - { 0xfe, 0x4c, KEY_8 }, - { 0xfe, 0x58, KEY_9 }, - { 0xfe, 0x13, KEY_ANGLE }, /* Aspect */ - { 0xfe, 0x03, KEY_0 }, - { 0xfe, 0x1f, KEY_ZOOM }, - { 0xfe, 0x43, KEY_REWIND }, - { 0xfe, 0x47, KEY_PLAYPAUSE }, - { 0xfe, 0x4f, KEY_FASTFORWARD }, - { 0xfe, 0x57, KEY_MUTE }, - { 0xfe, 0x0d, KEY_STOP }, - { 0xfe, 0x01, KEY_RECORD }, - { 0xfe, 0x4e, KEY_POWER }, + { 0xfe02, KEY_TV }, + { 0xfe0e, KEY_MP3 }, + { 0xfe1a, KEY_DVD }, + { 0xfe1e, KEY_FAVORITES }, + { 0xfe16, KEY_SETUP }, + { 0xfe46, KEY_POWER2 }, + { 0xfe0a, KEY_EPG }, + { 0xfe49, KEY_BACK }, + { 0xfe4d, KEY_MENU }, + { 0xfe51, KEY_UP }, + { 0xfe5b, KEY_LEFT }, + { 0xfe5f, KEY_RIGHT }, + { 0xfe53, KEY_DOWN }, + { 0xfe5e, KEY_OK }, + { 0xfe59, KEY_INFO }, + { 0xfe55, KEY_TAB }, + { 0xfe0f, KEY_PREVIOUSSONG },/* Replay */ + { 0xfe12, KEY_NEXTSONG }, /* Skip */ + { 0xfe42, KEY_ENTER }, /* Windows/Start */ + { 0xfe15, KEY_VOLUMEUP }, + { 0xfe05, KEY_VOLUMEDOWN }, + { 0xfe11, KEY_CHANNELUP }, + { 0xfe09, KEY_CHANNELDOWN }, + { 0xfe52, KEY_CAMERA }, + { 0xfe5a, KEY_TUNER }, /* Live */ + { 0xfe19, KEY_OPEN }, + { 0xfe0b, KEY_1 }, + { 0xfe17, KEY_2 }, + { 0xfe1b, KEY_3 }, + { 0xfe07, KEY_4 }, + { 0xfe50, KEY_5 }, + { 0xfe54, KEY_6 }, + { 0xfe48, KEY_7 }, + { 0xfe4c, KEY_8 }, + { 0xfe58, KEY_9 }, + { 0xfe13, KEY_ANGLE }, /* Aspect */ + { 0xfe03, KEY_0 }, + { 0xfe1f, KEY_ZOOM }, + { 0xfe43, KEY_REWIND }, + { 0xfe47, KEY_PLAYPAUSE }, + { 0xfe4f, KEY_FASTFORWARD }, + { 0xfe57, KEY_MUTE }, + { 0xfe0d, KEY_STOP }, + { 0xfe01, KEY_RECORD }, + { 0xfe4e, KEY_POWER }, }; static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { - { 0xfc, 0x02, KEY_SETUP }, /* Profile */ - { 0xfc, 0x43, KEY_POWER2 }, - { 0xfc, 0x06, KEY_EPG }, - { 0xfc, 0x5a, KEY_BACK }, - { 0xfc, 0x05, KEY_MENU }, - { 0xfc, 0x47, KEY_INFO }, - { 0xfc, 0x01, KEY_TAB }, - { 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */ - { 0xfc, 0x49, KEY_VOLUMEUP }, - { 0xfc, 0x09, KEY_VOLUMEDOWN }, - { 0xfc, 0x54, KEY_CHANNELUP }, - { 0xfc, 0x0b, KEY_CHANNELDOWN }, - { 0xfc, 0x16, KEY_CAMERA }, - { 0xfc, 0x40, KEY_TUNER }, /* ATV/DTV */ - { 0xfc, 0x45, KEY_OPEN }, - { 0xfc, 0x19, KEY_1 }, - { 0xfc, 0x18, KEY_2 }, - { 0xfc, 0x1b, KEY_3 }, - { 0xfc, 0x1a, KEY_4 }, - { 0xfc, 0x58, KEY_5 }, - { 0xfc, 0x59, KEY_6 }, - { 0xfc, 0x15, KEY_7 }, - { 0xfc, 0x14, KEY_8 }, - { 0xfc, 0x17, KEY_9 }, - { 0xfc, 0x44, KEY_ANGLE }, /* Aspect */ - { 0xfc, 0x55, KEY_0 }, - { 0xfc, 0x07, KEY_ZOOM }, - { 0xfc, 0x0a, KEY_REWIND }, - { 0xfc, 0x08, KEY_PLAYPAUSE }, - { 0xfc, 0x4b, KEY_FASTFORWARD }, - { 0xfc, 0x5b, KEY_MUTE }, - { 0xfc, 0x04, KEY_STOP }, - { 0xfc, 0x56, KEY_RECORD }, - { 0xfc, 0x57, KEY_POWER }, - { 0xfc, 0x41, KEY_UNKNOWN }, /* INPUT */ - { 0xfc, 0x00, KEY_UNKNOWN }, /* HD */ + { 0xfc02, KEY_SETUP }, /* Profile */ + { 0xfc43, KEY_POWER2 }, + { 0xfc06, KEY_EPG }, + { 0xfc5a, KEY_BACK }, + { 0xfc05, KEY_MENU }, + { 0xfc47, KEY_INFO }, + { 0xfc01, KEY_TAB }, + { 0xfc42, KEY_PREVIOUSSONG },/* Replay */ + { 0xfc49, KEY_VOLUMEUP }, + { 0xfc09, KEY_VOLUMEDOWN }, + { 0xfc54, KEY_CHANNELUP }, + { 0xfc0b, KEY_CHANNELDOWN }, + { 0xfc16, KEY_CAMERA }, + { 0xfc40, KEY_TUNER }, /* ATV/DTV */ + { 0xfc45, KEY_OPEN }, + { 0xfc19, KEY_1 }, + { 0xfc18, KEY_2 }, + { 0xfc1b, KEY_3 }, + { 0xfc1a, KEY_4 }, + { 0xfc58, KEY_5 }, + { 0xfc59, KEY_6 }, + { 0xfc15, KEY_7 }, + { 0xfc14, KEY_8 }, + { 0xfc17, KEY_9 }, + { 0xfc44, KEY_ANGLE }, /* Aspect */ + { 0xfc55, KEY_0 }, + { 0xfc07, KEY_ZOOM }, + { 0xfc0a, KEY_REWIND }, + { 0xfc08, KEY_PLAYPAUSE }, + { 0xfc4b, KEY_FASTFORWARD }, + { 0xfc5b, KEY_MUTE }, + { 0xfc04, KEY_STOP }, + { 0xfc56, KEY_RECORD }, + { 0xfc57, KEY_POWER }, + { 0xfc41, KEY_UNKNOWN }, /* INPUT */ + { 0xfc00, KEY_UNKNOWN }, /* HD */ }; static struct dvb_usb_rc_key d680_dmb_rc_keys[] = { - { 0x00, 0x38, KEY_UNKNOWN }, /* TV/AV */ - { 0x08, 0x0c, KEY_ZOOM }, - { 0x08, 0x00, KEY_0 }, - { 0x00, 0x01, KEY_1 }, - { 0x08, 0x02, KEY_2 }, - { 0x00, 0x03, KEY_3 }, - { 0x08, 0x04, KEY_4 }, - { 0x00, 0x05, KEY_5 }, - { 0x08, 0x06, KEY_6 }, - { 0x00, 0x07, KEY_7 }, - { 0x08, 0x08, KEY_8 }, - { 0x00, 0x09, KEY_9 }, - { 0x00, 0x0a, KEY_MUTE }, - { 0x08, 0x29, KEY_BACK }, - { 0x00, 0x12, KEY_CHANNELUP }, - { 0x08, 0x13, KEY_CHANNELDOWN }, - { 0x00, 0x2b, KEY_VOLUMEUP }, - { 0x08, 0x2c, KEY_VOLUMEDOWN }, - { 0x00, 0x20, KEY_UP }, - { 0x08, 0x21, KEY_DOWN }, - { 0x00, 0x11, KEY_LEFT }, - { 0x08, 0x10, KEY_RIGHT }, - { 0x00, 0x0d, KEY_OK }, - { 0x08, 0x1f, KEY_RECORD }, - { 0x00, 0x17, KEY_PLAYPAUSE }, - { 0x08, 0x16, KEY_PLAYPAUSE }, - { 0x00, 0x0b, KEY_STOP }, - { 0x08, 0x27, KEY_FASTFORWARD }, - { 0x00, 0x26, KEY_REWIND }, - { 0x08, 0x1e, KEY_UNKNOWN }, /* Time Shift */ - { 0x00, 0x0e, KEY_UNKNOWN }, /* Snapshot */ - { 0x08, 0x2d, KEY_UNKNOWN }, /* Mouse Cursor */ - { 0x00, 0x0f, KEY_UNKNOWN }, /* Minimize/Maximize */ - { 0x08, 0x14, KEY_UNKNOWN }, /* Shuffle */ - { 0x00, 0x25, KEY_POWER }, + { 0x0038, KEY_UNKNOWN }, /* TV/AV */ + { 0x080c, KEY_ZOOM }, + { 0x0800, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0802, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0804, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0806, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0808, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x0829, KEY_BACK }, + { 0x0012, KEY_CHANNELUP }, + { 0x0813, KEY_CHANNELDOWN }, + { 0x002b, KEY_VOLUMEUP }, + { 0x082c, KEY_VOLUMEDOWN }, + { 0x0020, KEY_UP }, + { 0x0821, KEY_DOWN }, + { 0x0011, KEY_LEFT }, + { 0x0810, KEY_RIGHT }, + { 0x000d, KEY_OK }, + { 0x081f, KEY_RECORD }, + { 0x0017, KEY_PLAYPAUSE }, + { 0x0816, KEY_PLAYPAUSE }, + { 0x000b, KEY_STOP }, + { 0x0827, KEY_FASTFORWARD }, + { 0x0026, KEY_REWIND }, + { 0x081e, KEY_UNKNOWN }, /* Time Shift */ + { 0x000e, KEY_UNKNOWN }, /* Snapshot */ + { 0x082d, KEY_UNKNOWN }, /* Mouse Cursor */ + { 0x000f, KEY_UNKNOWN }, /* Minimize/Maximize */ + { 0x0814, KEY_UNKNOWN }, /* Shuffle */ + { 0x0025, KEY_POWER }, }; static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) @@ -1094,8 +1094,18 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; } -static struct lgs8gl5_config lgs8gl5_cfg = { +static struct lgs8gxx_config d680_lgs8gl5_cfg = { + .prod = LGS8GXX_PROD_LGS8GL5, .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 0, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 5725, /* 5.725 MHz */ + .if_neg_center = 0, + .ext_adc = 0, + .adc_signed = 0, + .if_neg_edge = 0, }; static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) @@ -1135,7 +1145,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap); + adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); if (adap->fe == NULL) return -EIO; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 818b2ab584b..0b2812aa30a 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -4,13 +4,14 @@ * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 2. * - * Copyright (C) 2005-7 DiBcom, SA + * Copyright (C) 2005-9 DiBcom, SA et al */ #include "dib0700.h" #include "dib3000mc.h" #include "dib7000m.h" #include "dib7000p.h" +#include "dib8000.h" #include "mt2060.h" #include "mt2266.h" #include "tuner-xc2028.h" @@ -310,7 +311,7 @@ static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *tun_i2c; tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); return dvb_attach(mt2266_attach, adap->fe, tun_i2c, - &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;; + &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0; } /* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */ @@ -509,7 +510,8 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, return 0; } for (i=0;i<d->props.rc_key_map_size; i++) { - if (keymap[i].custom == key[3-2] && keymap[i].data == key[3-3]) { + if (rc5_custom(&keymap[i]) == key[3-2] && + rc5_data(&keymap[i]) == key[3-3]) { st->rc_counter = 0; *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; @@ -522,7 +524,8 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, default: { /* RC-5 protocol changes toggle bit on new keypress */ for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == key[3-2] && keymap[i].data == key[3-3]) { + if (rc5_custom(&keymap[i]) == key[3-2] && + rc5_data(&keymap[i]) == key[3-3]) { if (d->last_event == keymap[i].event && key[3-1] == st->rc_toggle) { st->rc_counter++; @@ -616,8 +619,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, /* Find the key in the map */ for (i = 0; i < d->props.rc_key_map_size; i++) { - if (keymap[i].custom == poll_reply.system_lsb && - keymap[i].data == poll_reply.data) { + if (rc5_custom(&keymap[i]) == poll_reply.system_lsb && + rc5_data(&keymap[i]) == poll_reply.data) { *event = keymap[i].event; found = 1; break; @@ -684,193 +687,193 @@ static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) static struct dvb_usb_rc_key dib0700_rc_keys[] = { /* Key codes for the tiny Pinnacle remote*/ - { 0x07, 0x00, KEY_MUTE }, - { 0x07, 0x01, KEY_MENU }, // Pinnacle logo - { 0x07, 0x39, KEY_POWER }, - { 0x07, 0x03, KEY_VOLUMEUP }, - { 0x07, 0x09, KEY_VOLUMEDOWN }, - { 0x07, 0x06, KEY_CHANNELUP }, - { 0x07, 0x0c, KEY_CHANNELDOWN }, - { 0x07, 0x0f, KEY_1 }, - { 0x07, 0x15, KEY_2 }, - { 0x07, 0x10, KEY_3 }, - { 0x07, 0x18, KEY_4 }, - { 0x07, 0x1b, KEY_5 }, - { 0x07, 0x1e, KEY_6 }, - { 0x07, 0x11, KEY_7 }, - { 0x07, 0x21, KEY_8 }, - { 0x07, 0x12, KEY_9 }, - { 0x07, 0x27, KEY_0 }, - { 0x07, 0x24, KEY_SCREEN }, // 'Square' key - { 0x07, 0x2a, KEY_TEXT }, // 'T' key - { 0x07, 0x2d, KEY_REWIND }, - { 0x07, 0x30, KEY_PLAY }, - { 0x07, 0x33, KEY_FASTFORWARD }, - { 0x07, 0x36, KEY_RECORD }, - { 0x07, 0x3c, KEY_STOP }, - { 0x07, 0x3f, KEY_CANCEL }, // '?' key + { 0x0700, KEY_MUTE }, + { 0x0701, KEY_MENU }, /* Pinnacle logo */ + { 0x0739, KEY_POWER }, + { 0x0703, KEY_VOLUMEUP }, + { 0x0709, KEY_VOLUMEDOWN }, + { 0x0706, KEY_CHANNELUP }, + { 0x070c, KEY_CHANNELDOWN }, + { 0x070f, KEY_1 }, + { 0x0715, KEY_2 }, + { 0x0710, KEY_3 }, + { 0x0718, KEY_4 }, + { 0x071b, KEY_5 }, + { 0x071e, KEY_6 }, + { 0x0711, KEY_7 }, + { 0x0721, KEY_8 }, + { 0x0712, KEY_9 }, + { 0x0727, KEY_0 }, + { 0x0724, KEY_SCREEN }, /* 'Square' key */ + { 0x072a, KEY_TEXT }, /* 'T' key */ + { 0x072d, KEY_REWIND }, + { 0x0730, KEY_PLAY }, + { 0x0733, KEY_FASTFORWARD }, + { 0x0736, KEY_RECORD }, + { 0x073c, KEY_STOP }, + { 0x073f, KEY_CANCEL }, /* '?' key */ /* Key codes for the Terratec Cinergy DT XS Diversity, similar to cinergyT2.c */ - { 0xeb, 0x01, KEY_POWER }, - { 0xeb, 0x02, KEY_1 }, - { 0xeb, 0x03, KEY_2 }, - { 0xeb, 0x04, KEY_3 }, - { 0xeb, 0x05, KEY_4 }, - { 0xeb, 0x06, KEY_5 }, - { 0xeb, 0x07, KEY_6 }, - { 0xeb, 0x08, KEY_7 }, - { 0xeb, 0x09, KEY_8 }, - { 0xeb, 0x0a, KEY_9 }, - { 0xeb, 0x0b, KEY_VIDEO }, - { 0xeb, 0x0c, KEY_0 }, - { 0xeb, 0x0d, KEY_REFRESH }, - { 0xeb, 0x0f, KEY_EPG }, - { 0xeb, 0x10, KEY_UP }, - { 0xeb, 0x11, KEY_LEFT }, - { 0xeb, 0x12, KEY_OK }, - { 0xeb, 0x13, KEY_RIGHT }, - { 0xeb, 0x14, KEY_DOWN }, - { 0xeb, 0x16, KEY_INFO }, - { 0xeb, 0x17, KEY_RED }, - { 0xeb, 0x18, KEY_GREEN }, - { 0xeb, 0x19, KEY_YELLOW }, - { 0xeb, 0x1a, KEY_BLUE }, - { 0xeb, 0x1b, KEY_CHANNELUP }, - { 0xeb, 0x1c, KEY_VOLUMEUP }, - { 0xeb, 0x1d, KEY_MUTE }, - { 0xeb, 0x1e, KEY_VOLUMEDOWN }, - { 0xeb, 0x1f, KEY_CHANNELDOWN }, - { 0xeb, 0x40, KEY_PAUSE }, - { 0xeb, 0x41, KEY_HOME }, - { 0xeb, 0x42, KEY_MENU }, /* DVD Menu */ - { 0xeb, 0x43, KEY_SUBTITLE }, - { 0xeb, 0x44, KEY_TEXT }, /* Teletext */ - { 0xeb, 0x45, KEY_DELETE }, - { 0xeb, 0x46, KEY_TV }, - { 0xeb, 0x47, KEY_DVD }, - { 0xeb, 0x48, KEY_STOP }, - { 0xeb, 0x49, KEY_VIDEO }, - { 0xeb, 0x4a, KEY_AUDIO }, /* Music */ - { 0xeb, 0x4b, KEY_SCREEN }, /* Pic */ - { 0xeb, 0x4c, KEY_PLAY }, - { 0xeb, 0x4d, KEY_BACK }, - { 0xeb, 0x4e, KEY_REWIND }, - { 0xeb, 0x4f, KEY_FASTFORWARD }, - { 0xeb, 0x54, KEY_PREVIOUS }, - { 0xeb, 0x58, KEY_RECORD }, - { 0xeb, 0x5c, KEY_NEXT }, + { 0xeb01, KEY_POWER }, + { 0xeb02, KEY_1 }, + { 0xeb03, KEY_2 }, + { 0xeb04, KEY_3 }, + { 0xeb05, KEY_4 }, + { 0xeb06, KEY_5 }, + { 0xeb07, KEY_6 }, + { 0xeb08, KEY_7 }, + { 0xeb09, KEY_8 }, + { 0xeb0a, KEY_9 }, + { 0xeb0b, KEY_VIDEO }, + { 0xeb0c, KEY_0 }, + { 0xeb0d, KEY_REFRESH }, + { 0xeb0f, KEY_EPG }, + { 0xeb10, KEY_UP }, + { 0xeb11, KEY_LEFT }, + { 0xeb12, KEY_OK }, + { 0xeb13, KEY_RIGHT }, + { 0xeb14, KEY_DOWN }, + { 0xeb16, KEY_INFO }, + { 0xeb17, KEY_RED }, + { 0xeb18, KEY_GREEN }, + { 0xeb19, KEY_YELLOW }, + { 0xeb1a, KEY_BLUE }, + { 0xeb1b, KEY_CHANNELUP }, + { 0xeb1c, KEY_VOLUMEUP }, + { 0xeb1d, KEY_MUTE }, + { 0xeb1e, KEY_VOLUMEDOWN }, + { 0xeb1f, KEY_CHANNELDOWN }, + { 0xeb40, KEY_PAUSE }, + { 0xeb41, KEY_HOME }, + { 0xeb42, KEY_MENU }, /* DVD Menu */ + { 0xeb43, KEY_SUBTITLE }, + { 0xeb44, KEY_TEXT }, /* Teletext */ + { 0xeb45, KEY_DELETE }, + { 0xeb46, KEY_TV }, + { 0xeb47, KEY_DVD }, + { 0xeb48, KEY_STOP }, + { 0xeb49, KEY_VIDEO }, + { 0xeb4a, KEY_AUDIO }, /* Music */ + { 0xeb4b, KEY_SCREEN }, /* Pic */ + { 0xeb4c, KEY_PLAY }, + { 0xeb4d, KEY_BACK }, + { 0xeb4e, KEY_REWIND }, + { 0xeb4f, KEY_FASTFORWARD }, + { 0xeb54, KEY_PREVIOUS }, + { 0xeb58, KEY_RECORD }, + { 0xeb5c, KEY_NEXT }, /* Key codes for the Haupauge WinTV Nova-TD, copied from nova-t-usb2.c (Nova-T USB2) */ - { 0x1e, 0x00, KEY_0 }, - { 0x1e, 0x01, KEY_1 }, - { 0x1e, 0x02, KEY_2 }, - { 0x1e, 0x03, KEY_3 }, - { 0x1e, 0x04, KEY_4 }, - { 0x1e, 0x05, KEY_5 }, - { 0x1e, 0x06, KEY_6 }, - { 0x1e, 0x07, KEY_7 }, - { 0x1e, 0x08, KEY_8 }, - { 0x1e, 0x09, KEY_9 }, - { 0x1e, 0x0a, KEY_KPASTERISK }, - { 0x1e, 0x0b, KEY_RED }, - { 0x1e, 0x0c, KEY_RADIO }, - { 0x1e, 0x0d, KEY_MENU }, - { 0x1e, 0x0e, KEY_GRAVE }, /* # */ - { 0x1e, 0x0f, KEY_MUTE }, - { 0x1e, 0x10, KEY_VOLUMEUP }, - { 0x1e, 0x11, KEY_VOLUMEDOWN }, - { 0x1e, 0x12, KEY_CHANNEL }, - { 0x1e, 0x14, KEY_UP }, - { 0x1e, 0x15, KEY_DOWN }, - { 0x1e, 0x16, KEY_LEFT }, - { 0x1e, 0x17, KEY_RIGHT }, - { 0x1e, 0x18, KEY_VIDEO }, - { 0x1e, 0x19, KEY_AUDIO }, - { 0x1e, 0x1a, KEY_MEDIA }, - { 0x1e, 0x1b, KEY_EPG }, - { 0x1e, 0x1c, KEY_TV }, - { 0x1e, 0x1e, KEY_NEXT }, - { 0x1e, 0x1f, KEY_BACK }, - { 0x1e, 0x20, KEY_CHANNELUP }, - { 0x1e, 0x21, KEY_CHANNELDOWN }, - { 0x1e, 0x24, KEY_LAST }, /* Skip backwards */ - { 0x1e, 0x25, KEY_OK }, - { 0x1e, 0x29, KEY_BLUE}, - { 0x1e, 0x2e, KEY_GREEN }, - { 0x1e, 0x30, KEY_PAUSE }, - { 0x1e, 0x32, KEY_REWIND }, - { 0x1e, 0x34, KEY_FASTFORWARD }, - { 0x1e, 0x35, KEY_PLAY }, - { 0x1e, 0x36, KEY_STOP }, - { 0x1e, 0x37, KEY_RECORD }, - { 0x1e, 0x38, KEY_YELLOW }, - { 0x1e, 0x3b, KEY_GOTO }, - { 0x1e, 0x3d, KEY_POWER }, + { 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_KPASTERISK }, + { 0x1e0b, KEY_RED }, + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_GRAVE }, /* # */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_CHANNEL }, + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, + { 0x1e19, KEY_AUDIO }, + { 0x1e1a, KEY_MEDIA }, + { 0x1e1b, KEY_EPG }, + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXT }, + { 0x1e1f, KEY_BACK }, + { 0x1e20, KEY_CHANNELUP }, + { 0x1e21, KEY_CHANNELDOWN }, + { 0x1e24, KEY_LAST }, /* Skip backwards */ + { 0x1e25, KEY_OK }, + { 0x1e29, KEY_BLUE}, + { 0x1e2e, KEY_GREEN }, + { 0x1e30, KEY_PAUSE }, + { 0x1e32, KEY_REWIND }, + { 0x1e34, KEY_FASTFORWARD }, + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, + { 0x1e38, KEY_YELLOW }, + { 0x1e3b, KEY_GOTO }, + { 0x1e3d, KEY_POWER }, /* Key codes for the Leadtek Winfast DTV Dongle */ - { 0x00, 0x42, KEY_POWER }, - { 0x07, 0x7c, KEY_TUNER }, - { 0x0f, 0x4e, KEY_PRINT }, /* PREVIEW */ - { 0x08, 0x40, KEY_SCREEN }, /* full screen toggle*/ - { 0x0f, 0x71, KEY_DOT }, /* frequency */ - { 0x07, 0x43, KEY_0 }, - { 0x0c, 0x41, KEY_1 }, - { 0x04, 0x43, KEY_2 }, - { 0x0b, 0x7f, KEY_3 }, - { 0x0e, 0x41, KEY_4 }, - { 0x06, 0x43, KEY_5 }, - { 0x09, 0x7f, KEY_6 }, - { 0x0d, 0x7e, KEY_7 }, - { 0x05, 0x7c, KEY_8 }, - { 0x0a, 0x40, KEY_9 }, - { 0x0e, 0x4e, KEY_CLEAR }, - { 0x04, 0x7c, KEY_CHANNEL }, /* show channel number */ - { 0x0f, 0x41, KEY_LAST }, /* recall */ - { 0x03, 0x42, KEY_MUTE }, - { 0x06, 0x4c, KEY_RESERVED }, /* PIP button*/ - { 0x01, 0x72, KEY_SHUFFLE }, /* SNAPSHOT */ - { 0x0c, 0x4e, KEY_PLAYPAUSE }, /* TIMESHIFT */ - { 0x0b, 0x70, KEY_RECORD }, - { 0x03, 0x7d, KEY_VOLUMEUP }, - { 0x01, 0x7d, KEY_VOLUMEDOWN }, - { 0x02, 0x42, KEY_CHANNELUP }, - { 0x00, 0x7d, KEY_CHANNELDOWN }, + { 0x0042, KEY_POWER }, + { 0x077c, KEY_TUNER }, + { 0x0f4e, KEY_PRINT }, /* PREVIEW */ + { 0x0840, KEY_SCREEN }, /* full screen toggle*/ + { 0x0f71, KEY_DOT }, /* frequency */ + { 0x0743, KEY_0 }, + { 0x0c41, KEY_1 }, + { 0x0443, KEY_2 }, + { 0x0b7f, KEY_3 }, + { 0x0e41, KEY_4 }, + { 0x0643, KEY_5 }, + { 0x097f, KEY_6 }, + { 0x0d7e, KEY_7 }, + { 0x057c, KEY_8 }, + { 0x0a40, KEY_9 }, + { 0x0e4e, KEY_CLEAR }, + { 0x047c, KEY_CHANNEL }, /* show channel number */ + { 0x0f41, KEY_LAST }, /* recall */ + { 0x0342, KEY_MUTE }, + { 0x064c, KEY_RESERVED }, /* PIP button*/ + { 0x0172, KEY_SHUFFLE }, /* SNAPSHOT */ + { 0x0c4e, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x0b70, KEY_RECORD }, + { 0x037d, KEY_VOLUMEUP }, + { 0x017d, KEY_VOLUMEDOWN }, + { 0x0242, KEY_CHANNELUP }, + { 0x007d, KEY_CHANNELDOWN }, /* Key codes for Nova-TD "credit card" remote control. */ - { 0x1d, 0x00, KEY_0 }, - { 0x1d, 0x01, KEY_1 }, - { 0x1d, 0x02, KEY_2 }, - { 0x1d, 0x03, KEY_3 }, - { 0x1d, 0x04, KEY_4 }, - { 0x1d, 0x05, KEY_5 }, - { 0x1d, 0x06, KEY_6 }, - { 0x1d, 0x07, KEY_7 }, - { 0x1d, 0x08, KEY_8 }, - { 0x1d, 0x09, KEY_9 }, - { 0x1d, 0x0a, KEY_TEXT }, - { 0x1d, 0x0d, KEY_MENU }, - { 0x1d, 0x0f, KEY_MUTE }, - { 0x1d, 0x10, KEY_VOLUMEUP }, - { 0x1d, 0x11, KEY_VOLUMEDOWN }, - { 0x1d, 0x12, KEY_CHANNEL }, - { 0x1d, 0x14, KEY_UP }, - { 0x1d, 0x15, KEY_DOWN }, - { 0x1d, 0x16, KEY_LEFT }, - { 0x1d, 0x17, KEY_RIGHT }, - { 0x1d, 0x1c, KEY_TV }, - { 0x1d, 0x1e, KEY_NEXT }, - { 0x1d, 0x1f, KEY_BACK }, - { 0x1d, 0x20, KEY_CHANNELUP }, - { 0x1d, 0x21, KEY_CHANNELDOWN }, - { 0x1d, 0x24, KEY_LAST }, - { 0x1d, 0x25, KEY_OK }, - { 0x1d, 0x30, KEY_PAUSE }, - { 0x1d, 0x32, KEY_REWIND }, - { 0x1d, 0x34, KEY_FASTFORWARD }, - { 0x1d, 0x35, KEY_PLAY }, - { 0x1d, 0x36, KEY_STOP }, - { 0x1d, 0x37, KEY_RECORD }, - { 0x1d, 0x3b, KEY_GOTO }, - { 0x1d, 0x3d, KEY_POWER }, + { 0x1d00, KEY_0 }, + { 0x1d01, KEY_1 }, + { 0x1d02, KEY_2 }, + { 0x1d03, KEY_3 }, + { 0x1d04, KEY_4 }, + { 0x1d05, KEY_5 }, + { 0x1d06, KEY_6 }, + { 0x1d07, KEY_7 }, + { 0x1d08, KEY_8 }, + { 0x1d09, KEY_9 }, + { 0x1d0a, KEY_TEXT }, + { 0x1d0d, KEY_MENU }, + { 0x1d0f, KEY_MUTE }, + { 0x1d10, KEY_VOLUMEUP }, + { 0x1d11, KEY_VOLUMEDOWN }, + { 0x1d12, KEY_CHANNEL }, + { 0x1d14, KEY_UP }, + { 0x1d15, KEY_DOWN }, + { 0x1d16, KEY_LEFT }, + { 0x1d17, KEY_RIGHT }, + { 0x1d1c, KEY_TV }, + { 0x1d1e, KEY_NEXT }, + { 0x1d1f, KEY_BACK }, + { 0x1d20, KEY_CHANNELUP }, + { 0x1d21, KEY_CHANNELDOWN }, + { 0x1d24, KEY_LAST }, + { 0x1d25, KEY_OK }, + { 0x1d30, KEY_PAUSE }, + { 0x1d32, KEY_REWIND }, + { 0x1d34, KEY_FASTFORWARD }, + { 0x1d35, KEY_PLAY }, + { 0x1d36, KEY_STOP }, + { 0x1d37, KEY_RECORD }, + { 0x1d3b, KEY_GOTO }, + { 0x1d3d, KEY_POWER }, }; /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ @@ -1096,11 +1099,13 @@ static struct dibx000_agc_config dib7070_agc_config = { static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) { + deb_info("reset: %d", onoff); return dib7000p_set_gpio(fe, 8, 0, !onoff); } static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) { + deb_info("sleep: %d", onoff); return dib7000p_set_gpio(fe, 9, 0, onoff); } @@ -1110,16 +1115,26 @@ static struct dib0070_config dib7070p_dib0070_config[2] = { .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - .clock_pad_drive = 4 + .clock_pad_drive = 4, + .charge_pump = 2, }, { .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - + .charge_pump = 2, } }; +static struct dib0070_config dib7770p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 0, + .flip_chip = 1, +}; + static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_adapter *adap = fe->dvb->priv; @@ -1137,6 +1152,45 @@ static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_fronte return state->set_param_save(fe, fep); } +static int dib7770_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + switch (band) { + case BAND_VHF: + dib7000p_set_gpio(fe, 0, 0, 1); + offset = 850; + break; + case BAND_UHF: + default: + dib7000p_set_gpio(fe, 0, 0, 0); + offset = 250; + break; + } + deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + return state->set_param_save(fe, fep); +} + +static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib7770p_dib0070_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override; + return 0; +} + static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; @@ -1215,6 +1269,306 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) return adap->fe == NULL ? -ENODEV : 0; } +/* DIB807x generic */ +static struct dibx000_agc_config dib807x_agc_config[2] = { + { + BAND_VHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0,P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (7 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup*/ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + }, { + BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (1 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup */ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + } +}; + +static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = { + 60000, 15000, /* internal, sampling*/ + 1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/ + 0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core, + ADClkSrc, modulo */ + (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ + (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ + 18179755, /* timf*/ + 12000000, /* xtal_hz*/ +}; + +static struct dib8000_config dib807x_dib8000_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + }, { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + } +}; + +static int dib807x_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 5, 0, !onoff); +} + +static int dib807x_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 0, 0, onoff); +} + +static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = { + { 240, 7}, + { 0xffff, 6}, +}; + +static struct dib0070_config dib807x_dib0070_config[2] = { + { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib807x_tuner_reset, + .sleep = dib807x_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 4, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -100, + .freq_offset_khz_vhf = -100, + }, { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib807x_tuner_reset, + .sleep = dib807x_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 2, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -25, + .freq_offset_khz_vhf = -25, + } +}; + +static int dib807x_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset = dib0070_wbd_offset(fe); + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + switch (band) { + case BAND_VHF: + offset += 750; + break; + case BAND_UHF: /* fall-thru wanted */ + default: + offset += 250; break; + } + deb_info("WBD for DiB8000: %d\n", offset); + dib8000_set_wbd_ref(fe, offset); + + return state->set_param_save(fe, fep); +} + +static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (adap->id == 0) { + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib807x_dib0070_config[0]) == NULL) + return -ENODEV; + } else { + if (dvb_attach(dib0070_attach, adap->fe, tun_i2c, + &dib807x_dib0070_config[1]) == NULL) + return -ENODEV; + } + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override; + return 0; +} + + +/* STK807x */ +static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +/* STK807xPVR */ +static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(500); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + /* initialize IC 0 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) +{ + /* initialize IC 1 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x82); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, + &dib807x_dib8000_config[1]); + + return adap->fe == NULL ? -ENODEV : 0; +} + + /* STK7070PD */ static struct dib7000p_config stk7070pd_dib7000p_config[2] = { { @@ -1497,6 +1851,16 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_H) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) }, +/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, +/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, + { USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1561,7 +1925,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, { "Leadtek Winfast DTV Dongle (STK7700P based)", - { &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] }, + { &dib0700_usb_id_table[8] }, { NULL }, }, { "AVerMedia AVerTV DVB-T Express", @@ -1624,7 +1988,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 4, + .num_device_descs = 5, .devices = { { "Pinnacle PCTV 2000e", { &dib0700_usb_id_table[11], NULL }, @@ -1642,6 +2006,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[14], NULL }, { NULL }, }, + { "YUAN High-Tech DiBcom STK7700D", + { &dib0700_usb_id_table[55], NULL }, + { NULL }, + }, }, @@ -1756,6 +2124,41 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 3, + .devices = { + { "Pinnacle PCTV 73A", + { &dib0700_usb_id_table[56], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 73e SE", + { &dib0700_usb_id_table[57], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 282e", + { &dib0700_usb_id_table[58], NULL }, + { NULL }, + }, + }, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dib0700_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_query = dib0700_rc_query + + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 2, .adapter = { { @@ -1822,7 +2225,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 8, + .num_device_descs = 9, .devices = { { "Terratec Cinergy HT USB XE", { &dib0700_usb_id_table[27], NULL }, @@ -1856,7 +2259,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[51], NULL }, { NULL }, }, - + { "YUAN High-Tech STK7700D", + { &dib0700_usb_id_table[54], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dib0700_rc_keys, @@ -1916,6 +2322,102 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7770p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 2, + .devices = { + { "DiBcom STK7770P reference design", + { &dib0700_usb_id_table[59], NULL }, + { NULL }, + }, + { "Terratec Cinergy T USB XXS (HD)", + { &dib0700_usb_id_table[34], &dib0700_usb_id_table[60] }, + { NULL }, + }, + }, + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dib0700_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_query = dib0700_rc_query + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .frontend_attach = stk807x_frontend_attach, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 2, + .devices = { + { "DiBcom STK807xP reference design", + { &dib0700_usb_id_table[62], NULL }, + { NULL }, + }, + { "Prolink Pixelview SBTVD", + { &dib0700_usb_id_table[63], NULL }, + { NULL }, + }, + }, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dib0700_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_query = dib0700_rc_query + + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 2, + .adapter = { + { + .frontend_attach = stk807xpvr_frontend_attach0, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + { + .frontend_attach = stk807xpvr_frontend_attach1, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK807xPVR reference design", + { &dib0700_usb_id_table[61], NULL }, + { NULL }, + }, + }, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dib0700_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_query = dib0700_rc_query }, }; diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index 8dbad1ec53c..da34979b533 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -318,132 +318,132 @@ EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); */ struct dvb_usb_rc_key dibusb_rc_keys[] = { /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ - { 0x00, 0x16, KEY_POWER }, - { 0x00, 0x10, KEY_MUTE }, - { 0x00, 0x03, KEY_1 }, - { 0x00, 0x01, KEY_2 }, - { 0x00, 0x06, KEY_3 }, - { 0x00, 0x09, KEY_4 }, - { 0x00, 0x1d, KEY_5 }, - { 0x00, 0x1f, KEY_6 }, - { 0x00, 0x0d, KEY_7 }, - { 0x00, 0x19, KEY_8 }, - { 0x00, 0x1b, KEY_9 }, - { 0x00, 0x15, KEY_0 }, - { 0x00, 0x05, KEY_CHANNELUP }, - { 0x00, 0x02, KEY_CHANNELDOWN }, - { 0x00, 0x1e, KEY_VOLUMEUP }, - { 0x00, 0x0a, KEY_VOLUMEDOWN }, - { 0x00, 0x11, KEY_RECORD }, - { 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ - { 0x00, 0x14, KEY_PLAY }, - { 0x00, 0x1a, KEY_STOP }, - { 0x00, 0x40, KEY_REWIND }, - { 0x00, 0x12, KEY_FASTFORWARD }, - { 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ - { 0x00, 0x4c, KEY_PAUSE }, - { 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */ - { 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + { 0x0016, KEY_POWER }, + { 0x0010, KEY_MUTE }, + { 0x0003, KEY_1 }, + { 0x0001, KEY_2 }, + { 0x0006, KEY_3 }, + { 0x0009, KEY_4 }, + { 0x001d, KEY_5 }, + { 0x001f, KEY_6 }, + { 0x000d, KEY_7 }, + { 0x0019, KEY_8 }, + { 0x001b, KEY_9 }, + { 0x0015, KEY_0 }, + { 0x0005, KEY_CHANNELUP }, + { 0x0002, KEY_CHANNELDOWN }, + { 0x001e, KEY_VOLUMEUP }, + { 0x000a, KEY_VOLUMEDOWN }, + { 0x0011, KEY_RECORD }, + { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x0014, KEY_PLAY }, + { 0x001a, KEY_STOP }, + { 0x0040, KEY_REWIND }, + { 0x0012, KEY_FASTFORWARD }, + { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x004c, KEY_PAUSE }, + { 0x004d, KEY_SCREEN }, /* Full screen mode. */ + { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ - { 0x00, 0x0c, KEY_CANCEL }, /* Cancel */ - { 0x00, 0x1c, KEY_EPG }, /* EPG */ - { 0x00, 0x00, KEY_TAB }, /* Tab */ - { 0x00, 0x48, KEY_INFO }, /* Preview */ - { 0x00, 0x04, KEY_LIST }, /* RecordList */ - { 0x00, 0x0f, KEY_TEXT }, /* Teletext */ + { 0x000c, KEY_CANCEL }, /* Cancel */ + { 0x001c, KEY_EPG }, /* EPG */ + { 0x0000, KEY_TAB }, /* Tab */ + { 0x0048, KEY_INFO }, /* Preview */ + { 0x0004, KEY_LIST }, /* RecordList */ + { 0x000f, KEY_TEXT }, /* Teletext */ /* Key codes for the KWorld/ADSTech/JetWay remote. */ - { 0x86, 0x12, KEY_POWER }, - { 0x86, 0x0f, KEY_SELECT }, /* source */ - { 0x86, 0x0c, KEY_UNKNOWN }, /* scan */ - { 0x86, 0x0b, KEY_EPG }, - { 0x86, 0x10, KEY_MUTE }, - { 0x86, 0x01, KEY_1 }, - { 0x86, 0x02, KEY_2 }, - { 0x86, 0x03, KEY_3 }, - { 0x86, 0x04, KEY_4 }, - { 0x86, 0x05, KEY_5 }, - { 0x86, 0x06, KEY_6 }, - { 0x86, 0x07, KEY_7 }, - { 0x86, 0x08, KEY_8 }, - { 0x86, 0x09, KEY_9 }, - { 0x86, 0x0a, KEY_0 }, - { 0x86, 0x18, KEY_ZOOM }, - { 0x86, 0x1c, KEY_UNKNOWN }, /* preview */ - { 0x86, 0x13, KEY_UNKNOWN }, /* snap */ - { 0x86, 0x00, KEY_UNDO }, - { 0x86, 0x1d, KEY_RECORD }, - { 0x86, 0x0d, KEY_STOP }, - { 0x86, 0x0e, KEY_PAUSE }, - { 0x86, 0x16, KEY_PLAY }, - { 0x86, 0x11, KEY_BACK }, - { 0x86, 0x19, KEY_FORWARD }, - { 0x86, 0x14, KEY_UNKNOWN }, /* pip */ - { 0x86, 0x15, KEY_ESC }, - { 0x86, 0x1a, KEY_UP }, - { 0x86, 0x1e, KEY_DOWN }, - { 0x86, 0x1f, KEY_LEFT }, - { 0x86, 0x1b, KEY_RIGHT }, + { 0x8612, KEY_POWER }, + { 0x860f, KEY_SELECT }, /* source */ + { 0x860c, KEY_UNKNOWN }, /* scan */ + { 0x860b, KEY_EPG }, + { 0x8610, KEY_MUTE }, + { 0x8601, KEY_1 }, + { 0x8602, KEY_2 }, + { 0x8603, KEY_3 }, + { 0x8604, KEY_4 }, + { 0x8605, KEY_5 }, + { 0x8606, KEY_6 }, + { 0x8607, KEY_7 }, + { 0x8608, KEY_8 }, + { 0x8609, KEY_9 }, + { 0x860a, KEY_0 }, + { 0x8618, KEY_ZOOM }, + { 0x861c, KEY_UNKNOWN }, /* preview */ + { 0x8613, KEY_UNKNOWN }, /* snap */ + { 0x8600, KEY_UNDO }, + { 0x861d, KEY_RECORD }, + { 0x860d, KEY_STOP }, + { 0x860e, KEY_PAUSE }, + { 0x8616, KEY_PLAY }, + { 0x8611, KEY_BACK }, + { 0x8619, KEY_FORWARD }, + { 0x8614, KEY_UNKNOWN }, /* pip */ + { 0x8615, KEY_ESC }, + { 0x861a, KEY_UP }, + { 0x861e, KEY_DOWN }, + { 0x861f, KEY_LEFT }, + { 0x861b, KEY_RIGHT }, /* Key codes for the DiBcom MOD3000 remote. */ - { 0x80, 0x00, KEY_MUTE }, - { 0x80, 0x01, KEY_TEXT }, - { 0x80, 0x02, KEY_HOME }, - { 0x80, 0x03, KEY_POWER }, - - { 0x80, 0x04, KEY_RED }, - { 0x80, 0x05, KEY_GREEN }, - { 0x80, 0x06, KEY_YELLOW }, - { 0x80, 0x07, KEY_BLUE }, - - { 0x80, 0x08, KEY_DVD }, - { 0x80, 0x09, KEY_AUDIO }, - { 0x80, 0x0a, KEY_MEDIA }, /* Pictures */ - { 0x80, 0x0b, KEY_VIDEO }, - - { 0x80, 0x0c, KEY_BACK }, - { 0x80, 0x0d, KEY_UP }, - { 0x80, 0x0e, KEY_RADIO }, - { 0x80, 0x0f, KEY_EPG }, - - { 0x80, 0x10, KEY_LEFT }, - { 0x80, 0x11, KEY_OK }, - { 0x80, 0x12, KEY_RIGHT }, - { 0x80, 0x13, KEY_UNKNOWN }, /* SAP */ - - { 0x80, 0x14, KEY_TV }, - { 0x80, 0x15, KEY_DOWN }, - { 0x80, 0x16, KEY_MENU }, /* DVD Menu */ - { 0x80, 0x17, KEY_LAST }, - - { 0x80, 0x18, KEY_RECORD }, - { 0x80, 0x19, KEY_STOP }, - { 0x80, 0x1a, KEY_PAUSE }, - { 0x80, 0x1b, KEY_PLAY }, - - { 0x80, 0x1c, KEY_PREVIOUS }, - { 0x80, 0x1d, KEY_REWIND }, - { 0x80, 0x1e, KEY_FASTFORWARD }, - { 0x80, 0x1f, KEY_NEXT}, - - { 0x80, 0x40, KEY_1 }, - { 0x80, 0x41, KEY_2 }, - { 0x80, 0x42, KEY_3 }, - { 0x80, 0x43, KEY_CHANNELUP }, - - { 0x80, 0x44, KEY_4 }, - { 0x80, 0x45, KEY_5 }, - { 0x80, 0x46, KEY_6 }, - { 0x80, 0x47, KEY_CHANNELDOWN }, - - { 0x80, 0x48, KEY_7 }, - { 0x80, 0x49, KEY_8 }, - { 0x80, 0x4a, KEY_9 }, - { 0x80, 0x4b, KEY_VOLUMEUP }, - - { 0x80, 0x4c, KEY_CLEAR }, - { 0x80, 0x4d, KEY_0 }, - { 0x80, 0x4e, KEY_ENTER }, - { 0x80, 0x4f, KEY_VOLUMEDOWN }, + { 0x8000, KEY_MUTE }, + { 0x8001, KEY_TEXT }, + { 0x8002, KEY_HOME }, + { 0x8003, KEY_POWER }, + + { 0x8004, KEY_RED }, + { 0x8005, KEY_GREEN }, + { 0x8006, KEY_YELLOW }, + { 0x8007, KEY_BLUE }, + + { 0x8008, KEY_DVD }, + { 0x8009, KEY_AUDIO }, + { 0x800a, KEY_MEDIA }, /* Pictures */ + { 0x800b, KEY_VIDEO }, + + { 0x800c, KEY_BACK }, + { 0x800d, KEY_UP }, + { 0x800e, KEY_RADIO }, + { 0x800f, KEY_EPG }, + + { 0x8010, KEY_LEFT }, + { 0x8011, KEY_OK }, + { 0x8012, KEY_RIGHT }, + { 0x8013, KEY_UNKNOWN }, /* SAP */ + + { 0x8014, KEY_TV }, + { 0x8015, KEY_DOWN }, + { 0x8016, KEY_MENU }, /* DVD Menu */ + { 0x8017, KEY_LAST }, + + { 0x8018, KEY_RECORD }, + { 0x8019, KEY_STOP }, + { 0x801a, KEY_PAUSE }, + { 0x801b, KEY_PLAY }, + + { 0x801c, KEY_PREVIOUS }, + { 0x801d, KEY_REWIND }, + { 0x801e, KEY_FASTFORWARD }, + { 0x801f, KEY_NEXT}, + + { 0x8040, KEY_1 }, + { 0x8041, KEY_2 }, + { 0x8042, KEY_3 }, + { 0x8043, KEY_CHANNELUP }, + + { 0x8044, KEY_4 }, + { 0x8045, KEY_5 }, + { 0x8046, KEY_6 }, + { 0x8047, KEY_CHANNELDOWN }, + + { 0x8048, KEY_7 }, + { 0x8049, KEY_8 }, + { 0x804a, KEY_9 }, + { 0x804b, KEY_VOLUMEUP }, + + { 0x804c, KEY_CLEAR }, + { 0x804d, KEY_0 }, + { 0x804e, KEY_ENTER }, + { 0x804f, KEY_VOLUMEDOWN }, }; EXPORT_SYMBOL(dibusb_rc_keys); diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index 059cec95531..a05b9f87566 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -42,6 +42,8 @@ static struct usb_device_id dibusb_dib3000mc_table [] = { /* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) }, /* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) }, /* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) }, +/* 14 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) }, +/* 15 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table); @@ -66,7 +68,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, + .count = 8, .endpoint = 0x06, .u = { .bulk = { @@ -88,7 +90,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .num_device_descs = 7, + .num_device_descs = 8, .devices = { { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", { &dibusb_dib3000mc_table[0], NULL }, @@ -119,6 +121,10 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { { &dibusb_dib3000mc_table[12], NULL }, { &dibusb_dib3000mc_table[13], NULL }, }, + { "Humax/Coex DVB-T USB Stick 2.0 High Speed", + { &dibusb_dib3000mc_table[14], NULL }, + { &dibusb_dib3000mc_table[15], NULL }, + }, { NULL }, } }; diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index b545cf3eab2..955147d0075 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -162,61 +162,61 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap) } static struct dvb_usb_rc_key digitv_rc_keys[] = { - { 0x5f, 0x55, KEY_0 }, - { 0x6f, 0x55, KEY_1 }, - { 0x9f, 0x55, KEY_2 }, - { 0xaf, 0x55, KEY_3 }, - { 0x5f, 0x56, KEY_4 }, - { 0x6f, 0x56, KEY_5 }, - { 0x9f, 0x56, KEY_6 }, - { 0xaf, 0x56, KEY_7 }, - { 0x5f, 0x59, KEY_8 }, - { 0x6f, 0x59, KEY_9 }, - { 0x9f, 0x59, KEY_TV }, - { 0xaf, 0x59, KEY_AUX }, - { 0x5f, 0x5a, KEY_DVD }, - { 0x6f, 0x5a, KEY_POWER }, - { 0x9f, 0x5a, KEY_MHP }, /* labelled 'Picture' */ - { 0xaf, 0x5a, KEY_AUDIO }, - { 0x5f, 0x65, KEY_INFO }, - { 0x6f, 0x65, KEY_F13 }, /* 16:9 */ - { 0x9f, 0x65, KEY_F14 }, /* 14:9 */ - { 0xaf, 0x65, KEY_EPG }, - { 0x5f, 0x66, KEY_EXIT }, - { 0x6f, 0x66, KEY_MENU }, - { 0x9f, 0x66, KEY_UP }, - { 0xaf, 0x66, KEY_DOWN }, - { 0x5f, 0x69, KEY_LEFT }, - { 0x6f, 0x69, KEY_RIGHT }, - { 0x9f, 0x69, KEY_ENTER }, - { 0xaf, 0x69, KEY_CHANNELUP }, - { 0x5f, 0x6a, KEY_CHANNELDOWN }, - { 0x6f, 0x6a, KEY_VOLUMEUP }, - { 0x9f, 0x6a, KEY_VOLUMEDOWN }, - { 0xaf, 0x6a, KEY_RED }, - { 0x5f, 0x95, KEY_GREEN }, - { 0x6f, 0x95, KEY_YELLOW }, - { 0x9f, 0x95, KEY_BLUE }, - { 0xaf, 0x95, KEY_SUBTITLE }, - { 0x5f, 0x96, KEY_F15 }, /* AD */ - { 0x6f, 0x96, KEY_TEXT }, - { 0x9f, 0x96, KEY_MUTE }, - { 0xaf, 0x96, KEY_REWIND }, - { 0x5f, 0x99, KEY_STOP }, - { 0x6f, 0x99, KEY_PLAY }, - { 0x9f, 0x99, KEY_FASTFORWARD }, - { 0xaf, 0x99, KEY_F16 }, /* chapter */ - { 0x5f, 0x9a, KEY_PAUSE }, - { 0x6f, 0x9a, KEY_PLAY }, - { 0x9f, 0x9a, KEY_RECORD }, - { 0xaf, 0x9a, KEY_F17 }, /* picture in picture */ - { 0x5f, 0xa5, KEY_KPPLUS }, /* zoom in */ - { 0x6f, 0xa5, KEY_KPMINUS }, /* zoom out */ - { 0x9f, 0xa5, KEY_F18 }, /* capture */ - { 0xaf, 0xa5, KEY_F19 }, /* web */ - { 0x5f, 0xa6, KEY_EMAIL }, - { 0x6f, 0xa6, KEY_PHONE }, - { 0x9f, 0xa6, KEY_PC }, + { 0x5f55, KEY_0 }, + { 0x6f55, KEY_1 }, + { 0x9f55, KEY_2 }, + { 0xaf55, KEY_3 }, + { 0x5f56, KEY_4 }, + { 0x6f56, KEY_5 }, + { 0x9f56, KEY_6 }, + { 0xaf56, KEY_7 }, + { 0x5f59, KEY_8 }, + { 0x6f59, KEY_9 }, + { 0x9f59, KEY_TV }, + { 0xaf59, KEY_AUX }, + { 0x5f5a, KEY_DVD }, + { 0x6f5a, KEY_POWER }, + { 0x9f5a, KEY_MHP }, /* labelled 'Picture' */ + { 0xaf5a, KEY_AUDIO }, + { 0x5f65, KEY_INFO }, + { 0x6f65, KEY_F13 }, /* 16:9 */ + { 0x9f65, KEY_F14 }, /* 14:9 */ + { 0xaf65, KEY_EPG }, + { 0x5f66, KEY_EXIT }, + { 0x6f66, KEY_MENU }, + { 0x9f66, KEY_UP }, + { 0xaf66, KEY_DOWN }, + { 0x5f69, KEY_LEFT }, + { 0x6f69, KEY_RIGHT }, + { 0x9f69, KEY_ENTER }, + { 0xaf69, KEY_CHANNELUP }, + { 0x5f6a, KEY_CHANNELDOWN }, + { 0x6f6a, KEY_VOLUMEUP }, + { 0x9f6a, KEY_VOLUMEDOWN }, + { 0xaf6a, KEY_RED }, + { 0x5f95, KEY_GREEN }, + { 0x6f95, KEY_YELLOW }, + { 0x9f95, KEY_BLUE }, + { 0xaf95, KEY_SUBTITLE }, + { 0x5f96, KEY_F15 }, /* AD */ + { 0x6f96, KEY_TEXT }, + { 0x9f96, KEY_MUTE }, + { 0xaf96, KEY_REWIND }, + { 0x5f99, KEY_STOP }, + { 0x6f99, KEY_PLAY }, + { 0x9f99, KEY_FASTFORWARD }, + { 0xaf99, KEY_F16 }, /* chapter */ + { 0x5f9a, KEY_PAUSE }, + { 0x6f9a, KEY_PLAY }, + { 0x9f9a, KEY_RECORD }, + { 0xaf9a, KEY_F17 }, /* picture in picture */ + { 0x5fa5, KEY_KPPLUS }, /* zoom in */ + { 0x6fa5, KEY_KPMINUS }, /* zoom out */ + { 0x9fa5, KEY_F18 }, /* capture */ + { 0xafa5, KEY_F19 }, /* web */ + { 0x5fa6, KEY_EMAIL }, + { 0x6fa6, KEY_PHONE }, + { 0x9fa6, KEY_PC }, }; static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) @@ -238,8 +238,8 @@ static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if (key[1] != 0) { for (i = 0; i < d->props.rc_key_map_size; i++) { - if (d->props.rc_key_map[i].custom == key[1] && - d->props.rc_key_map[i].data == key[2]) { + if (rc5_custom(&d->props.rc_key_map[i]) == key[1] && + rc5_data(&d->props.rc_key_map[i]) == key[2]) { *event = d->props.rc_key_map[i].event; *state = REMOTE_KEY_PRESSED; return 0; diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index 81a6cbf6016..a1b12b01cbe 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -58,24 +58,24 @@ static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, /* remote control */ /* key list for the tiny remote control (Yakumo, don't know about the others) */ static struct dvb_usb_rc_key dtt200u_rc_keys[] = { - { 0x80, 0x01, KEY_MUTE }, - { 0x80, 0x02, KEY_CHANNELDOWN }, - { 0x80, 0x03, KEY_VOLUMEDOWN }, - { 0x80, 0x04, KEY_1 }, - { 0x80, 0x05, KEY_2 }, - { 0x80, 0x06, KEY_3 }, - { 0x80, 0x07, KEY_4 }, - { 0x80, 0x08, KEY_5 }, - { 0x80, 0x09, KEY_6 }, - { 0x80, 0x0a, KEY_7 }, - { 0x80, 0x0c, KEY_ZOOM }, - { 0x80, 0x0d, KEY_0 }, - { 0x80, 0x0e, KEY_SELECT }, - { 0x80, 0x12, KEY_POWER }, - { 0x80, 0x1a, KEY_CHANNELUP }, - { 0x80, 0x1b, KEY_8 }, - { 0x80, 0x1e, KEY_VOLUMEUP }, - { 0x80, 0x1f, KEY_9 }, + { 0x8001, KEY_MUTE }, + { 0x8002, KEY_CHANNELDOWN }, + { 0x8003, KEY_VOLUMEDOWN }, + { 0x8004, KEY_1 }, + { 0x8005, KEY_2 }, + { 0x8006, KEY_3 }, + { 0x8007, KEY_4 }, + { 0x8008, KEY_5 }, + { 0x8009, KEY_6 }, + { 0x800a, KEY_7 }, + { 0x800c, KEY_ZOOM }, + { 0x800d, KEY_0 }, + { 0x800e, KEY_SELECT }, + { 0x8012, KEY_POWER }, + { 0x801a, KEY_CHANNELUP }, + { 0x801b, KEY_8 }, + { 0x801e, KEY_VOLUMEUP }, + { 0x801f, KEY_9 }, }; static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index 326f7608954..cead089bbb4 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -19,7 +19,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d) return -EINVAL; } - strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); + strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); d->i2c_adap.class = I2C_CLASS_TV_DIGITAL, d->i2c_adap.algo = d->props.i2c_algo; d->i2c_adap.algo_data = NULL; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 9593b728999..a548c14c194 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -46,6 +46,7 @@ #define USB_VID_MSI_2 0x1462 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 +#define USB_VID_PIXELVIEW 0x1554 #define USB_VID_TECHNOTREND 0x0b48 #define USB_VID_TERRATEC 0x0ccd #define USB_VID_TELESTAR 0x10b9 @@ -58,6 +59,8 @@ #define USB_VID_GIGABYTE 0x1044 #define USB_VID_YUAN 0x1164 #define USB_VID_XTENSIONS 0x1ae7 +#define USB_VID_HUMAX_COEX 0x10b9 +#define USB_VID_774 0x7a69 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 @@ -94,7 +97,10 @@ #define USB_PID_DIBCOM_STK7700_U7000 0x7001 #define USB_PID_DIBCOM_STK7070P 0x1ebc #define USB_PID_DIBCOM_STK7070PD 0x1ebe +#define USB_PID_DIBCOM_STK807XP 0x1f90 +#define USB_PID_DIBCOM_STK807XPVR 0x1f98 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 #define USB_PID_UNIWILL_STK7700P 0x6003 @@ -103,6 +109,7 @@ #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_INTEL_CE9500 0x9500 #define USB_PID_KWORLD_399U 0xe399 +#define USB_PID_KWORLD_399U_2 0xe400 #define USB_PID_KWORLD_395U 0xe396 #define USB_PID_KWORLD_395U_2 0xe39b #define USB_PID_KWORLD_395U_3 0xe395 @@ -182,6 +189,7 @@ #define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060 #define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062 #define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078 +#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab #define USB_PID_TERRATEC_T3 0x10a0 #define USB_PID_TERRATEC_T5 0x10a1 #define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e @@ -193,6 +201,10 @@ #define USB_PID_PINNACLE_PCTV73E 0x0237 #define USB_PID_PINNACLE_PCTV801E 0x023a #define USB_PID_PINNACLE_PCTV801E_SE 0x023b +#define USB_PID_PINNACLE_PCTV73A 0x0243 +#define USB_PID_PINNACLE_PCTV73ESE 0x0245 +#define USB_PID_PINNACLE_PCTV282E 0x0248 +#define USB_PID_PIXELVIEW_SBTVD 0x5010 #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f #define USB_PID_PCTV_450E 0x0222 @@ -252,6 +264,8 @@ #define USB_PID_YUAN_STK7700PH 0x1f08 #define USB_PID_YUAN_PD378S 0x2edc #define USB_PID_YUAN_MC770 0x0871 +#define USB_PID_YUAN_STK7700D 0x1efc +#define USB_PID_YUAN_STK7700D_2 0x1e8c #define USB_PID_DW2102 0x2102 #define USB_PID_XTENSIONS_XD_380 0x0381 #define USB_PID_TELESTAR_STARSTICK_2 0x8000 @@ -259,5 +273,8 @@ #define USB_PID_SONY_PLAYTV 0x0003 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 +#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 +#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 +#define USB_PID_FRIIO_WHITE 0x0001 #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index c0c2c22ddd8..edde87c6aa3 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -8,6 +8,71 @@ #include "dvb-usb-common.h" #include <linux/usb/input.h> +static int dvb_usb_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + struct dvb_usb_device *d = input_get_drvdata(dev); + + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + int i; + + /* See if we can match the raw key code. */ + for (i = 0; i < d->props.rc_key_map_size; i++) + if (keymap[i].scan == scancode) { + *keycode = keymap[i].event; + return 0; + } + + /* + * If is there extra space, returns KEY_RESERVED, + * otherwise, input core won't let dvb_usb_setkeycode + * to work + */ + for (i = 0; i < d->props.rc_key_map_size; i++) + if (keymap[i].event == KEY_RESERVED || + keymap[i].event == KEY_UNKNOWN) { + *keycode = KEY_RESERVED; + return 0; + } + + return -EINVAL; +} + +static int dvb_usb_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + struct dvb_usb_device *d = input_get_drvdata(dev); + + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + int i; + + /* Search if it is replacing an existing keycode */ + for (i = 0; i < d->props.rc_key_map_size; i++) + if (keymap[i].scan == scancode) { + keymap[i].event = keycode; + return 0; + } + + /* Search if is there a clean entry. If so, use it */ + for (i = 0; i < d->props.rc_key_map_size; i++) + if (keymap[i].event == KEY_RESERVED || + keymap[i].event == KEY_UNKNOWN) { + keymap[i].scan = scancode; + keymap[i].event = 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; +} + /* Remote-control poll function - called every dib->rc_query_interval ms to see * whether the remote control has received anything. * @@ -111,6 +176,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) input_dev->phys = d->rc_phys; usb_to_input_id(d->udev, &input_dev->id); input_dev->dev.parent = &d->udev->dev; + input_dev->getkeycode = dvb_usb_getkeycode; + input_dev->setkeycode = dvb_usb_setkeycode; /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc_key_map_size); @@ -128,6 +195,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) input_dev->rep[REP_PERIOD] = d->props.rc_interval; input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; + input_set_drvdata(input_dev, d); + err = input_register_device(input_dev); if (err) { input_free_device(input_dev); @@ -178,8 +247,8 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, } /* See if we can match the raw key code. */ for (i = 0; i < d->props.rc_key_map_size; i++) - if (keymap[i].custom == keybuf[1] && - keymap[i].data == keybuf[3]) { + if (rc5_custom(&keymap[i]) == keybuf[1] && + rc5_data(&keymap[i]) == keybuf[3]) { *event = keymap[i].event; *state = REMOTE_KEY_PRESSED; return 0; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index e441d274e6c..fe2b87efb3f 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -81,10 +81,25 @@ struct dvb_usb_device_description { * @event: the input event assigned to key identified by custom and data */ struct dvb_usb_rc_key { - u8 custom,data; + u16 scan; u32 event; }; +static inline u8 rc5_custom(struct dvb_usb_rc_key *key) +{ + return (key->scan >> 8) & 0xff; +} + +static inline u8 rc5_data(struct dvb_usb_rc_key *key) +{ + return key->scan & 0xff; +} + +static inline u8 rc5_scan(struct dvb_usb_rc_key *key) +{ + return key->scan & 0xffff; +} + struct dvb_usb_device; struct dvb_usb_adapter; struct usb_data_stream; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 75de49c0d94..5bb9479d154 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,6 +1,6 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, -* TeVii S600, S650 Cards +* TeVii S600, S630, S650 Cards * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +18,8 @@ #include "eds1547.h" #include "cx24116.h" #include "tda1002x.h" +#include "mt312.h" +#include "zl10039.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -39,6 +41,10 @@ #define USB_PID_TEVII_S650 0xd650 #endif +#ifndef USB_PID_TEVII_S630 +#define USB_PID_TEVII_S630 0xd630 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -436,6 +442,69 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], return num; } +static int s630_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: { /* read */ + u8 ibuf[msg[1].len], obuf[3]; + obuf[0] = msg[1].len; + obuf[1] = (msg[0].addr << 1); + obuf[2] = msg[0].buf[0]; + + ret = dw210x_op_rw(d->udev, 0x90, 0, 0, + obuf, 3, DW210X_WRITE_MSG); + msleep(5); + ret = dw210x_op_rw(d->udev, 0x91, 0, 0, + ibuf, msg[1].len, DW210X_READ_MSG); + memcpy(msg[1].buf, ibuf, msg[1].len); + break; + } + case 1: + switch (msg[0].addr) { + case 0x60: + case 0x0e: { + /* write to zl10313, zl10039 register, */ + u8 obuf[msg[0].len + 2]; + obuf[0] = msg[0].len + 1; + obuf[1] = (msg[0].addr << 1); + memcpy(obuf + 2, msg[0].buf, msg[0].len); + ret = dw210x_op_rw(d->udev, 0x80, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + break; + } + case (DW2102_RC_QUERY): { + u8 ibuf[4]; + ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 4, DW210X_READ_MSG); + msg[0].buf[0] = ibuf[3]; + break; + } + case (DW2102_VOLTAGE_CTRL): { + u8 obuf[2]; + obuf[0] = 0x03; + obuf[1] = msg[0].buf[0]; + ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + static u32 dw210x_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; @@ -466,6 +535,11 @@ static struct i2c_algorithm dw3101_i2c_algo = { .functionality = dw210x_i2c_func, }; +static struct i2c_algorithm s630_i2c_algo = { + .master_xfer = s630_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { int i; @@ -490,6 +564,37 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) return 0; }; +static int s630_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i, ret; + u8 buf[3], eeprom[256], eepromline[16]; + + for (i = 0; i < 256; i++) { + buf[0] = 1; + buf[1] = 0xa0; + buf[2] = i; + ret = dw210x_op_rw(d->udev, 0x90, 0, 0, + buf, 3, DW210X_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0x91, 0, 0, + buf, 1, DW210X_READ_MSG); + if (ret < 0) { + err("read eeprom failed."); + return -1; + } else { + eepromline[i % 16] = buf[0]; + eeprom[i] = buf[0]; + } + + if ((i % 16) == 15) { + deb_xfer("%02x: ", i - 15); + debug_dump(eepromline, 16, deb_xfer); + } + } + + memcpy(mac, eeprom + 16, 6); + return 0; +}; + static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { static u8 command_13v[1] = {0x00}; @@ -535,6 +640,10 @@ static struct tda10023_config dw3101_tda10023_config = { .invert = 1, }; +static struct mt312_config zl313_config = { + .demod_address = 0x0e, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config, @@ -596,6 +705,18 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d) return -EIO; } +static int s630_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe = dvb_attach(mt312_attach, &zl313_config, + &d->dev->i2c_adap); + if (d->fe != NULL) { + d->fe->ops.set_voltage = dw210x_set_voltage; + info("Attached zl10313!\n"); + return 0; + } + return -EIO; +} + static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x60, @@ -619,123 +740,131 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int s630_zl10039_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(zl10039_attach, adap->fe, 0x60, + &adap->dev->i2c_adap); + + return 0; +} + static struct dvb_usb_rc_key dw210x_rc_keys[] = { - { 0xf8, 0x0a, KEY_Q }, /*power*/ - { 0xf8, 0x0c, KEY_M }, /*mute*/ - { 0xf8, 0x11, KEY_1 }, - { 0xf8, 0x12, KEY_2 }, - { 0xf8, 0x13, KEY_3 }, - { 0xf8, 0x14, KEY_4 }, - { 0xf8, 0x15, KEY_5 }, - { 0xf8, 0x16, KEY_6 }, - { 0xf8, 0x17, KEY_7 }, - { 0xf8, 0x18, KEY_8 }, - { 0xf8, 0x19, KEY_9 }, - { 0xf8, 0x10, KEY_0 }, - { 0xf8, 0x1c, KEY_PAGEUP }, /*ch+*/ - { 0xf8, 0x0f, KEY_PAGEDOWN }, /*ch-*/ - { 0xf8, 0x1a, KEY_O }, /*vol+*/ - { 0xf8, 0x0e, KEY_Z }, /*vol-*/ - { 0xf8, 0x04, KEY_R }, /*rec*/ - { 0xf8, 0x09, KEY_D }, /*fav*/ - { 0xf8, 0x08, KEY_BACKSPACE }, /*rewind*/ - { 0xf8, 0x07, KEY_A }, /*fast*/ - { 0xf8, 0x0b, KEY_P }, /*pause*/ - { 0xf8, 0x02, KEY_ESC }, /*cancel*/ - { 0xf8, 0x03, KEY_G }, /*tab*/ - { 0xf8, 0x00, KEY_UP }, /*up*/ - { 0xf8, 0x1f, KEY_ENTER }, /*ok*/ - { 0xf8, 0x01, KEY_DOWN }, /*down*/ - { 0xf8, 0x05, KEY_C }, /*cap*/ - { 0xf8, 0x06, KEY_S }, /*stop*/ - { 0xf8, 0x40, KEY_F }, /*full*/ - { 0xf8, 0x1e, KEY_W }, /*tvmode*/ - { 0xf8, 0x1b, KEY_B }, /*recall*/ + { 0xf80a, KEY_Q }, /*power*/ + { 0xf80c, KEY_M }, /*mute*/ + { 0xf811, KEY_1 }, + { 0xf812, KEY_2 }, + { 0xf813, KEY_3 }, + { 0xf814, KEY_4 }, + { 0xf815, KEY_5 }, + { 0xf816, KEY_6 }, + { 0xf817, KEY_7 }, + { 0xf818, KEY_8 }, + { 0xf819, KEY_9 }, + { 0xf810, KEY_0 }, + { 0xf81c, KEY_PAGEUP }, /*ch+*/ + { 0xf80f, KEY_PAGEDOWN }, /*ch-*/ + { 0xf81a, KEY_O }, /*vol+*/ + { 0xf80e, KEY_Z }, /*vol-*/ + { 0xf804, KEY_R }, /*rec*/ + { 0xf809, KEY_D }, /*fav*/ + { 0xf808, KEY_BACKSPACE }, /*rewind*/ + { 0xf807, KEY_A }, /*fast*/ + { 0xf80b, KEY_P }, /*pause*/ + { 0xf802, KEY_ESC }, /*cancel*/ + { 0xf803, KEY_G }, /*tab*/ + { 0xf800, KEY_UP }, /*up*/ + { 0xf81f, KEY_ENTER }, /*ok*/ + { 0xf801, KEY_DOWN }, /*down*/ + { 0xf805, KEY_C }, /*cap*/ + { 0xf806, KEY_S }, /*stop*/ + { 0xf840, KEY_F }, /*full*/ + { 0xf81e, KEY_W }, /*tvmode*/ + { 0xf81b, KEY_B }, /*recall*/ }; static struct dvb_usb_rc_key tevii_rc_keys[] = { - { 0xf8, 0x0a, KEY_POWER }, - { 0xf8, 0x0c, KEY_MUTE }, - { 0xf8, 0x11, KEY_1 }, - { 0xf8, 0x12, KEY_2 }, - { 0xf8, 0x13, KEY_3 }, - { 0xf8, 0x14, KEY_4 }, - { 0xf8, 0x15, KEY_5 }, - { 0xf8, 0x16, KEY_6 }, - { 0xf8, 0x17, KEY_7 }, - { 0xf8, 0x18, KEY_8 }, - { 0xf8, 0x19, KEY_9 }, - { 0xf8, 0x10, KEY_0 }, - { 0xf8, 0x1c, KEY_MENU }, - { 0xf8, 0x0f, KEY_VOLUMEDOWN }, - { 0xf8, 0x1a, KEY_LAST }, - { 0xf8, 0x0e, KEY_OPEN }, - { 0xf8, 0x04, KEY_RECORD }, - { 0xf8, 0x09, KEY_VOLUMEUP }, - { 0xf8, 0x08, KEY_CHANNELUP }, - { 0xf8, 0x07, KEY_PVR }, - { 0xf8, 0x0b, KEY_TIME }, - { 0xf8, 0x02, KEY_RIGHT }, - { 0xf8, 0x03, KEY_LEFT }, - { 0xf8, 0x00, KEY_UP }, - { 0xf8, 0x1f, KEY_OK }, - { 0xf8, 0x01, KEY_DOWN }, - { 0xf8, 0x05, KEY_TUNER }, - { 0xf8, 0x06, KEY_CHANNELDOWN }, - { 0xf8, 0x40, KEY_PLAYPAUSE }, - { 0xf8, 0x1e, KEY_REWIND }, - { 0xf8, 0x1b, KEY_FAVORITES }, - { 0xf8, 0x1d, KEY_BACK }, - { 0xf8, 0x4d, KEY_FASTFORWARD }, - { 0xf8, 0x44, KEY_EPG }, - { 0xf8, 0x4c, KEY_INFO }, - { 0xf8, 0x41, KEY_AB }, - { 0xf8, 0x43, KEY_AUDIO }, - { 0xf8, 0x45, KEY_SUBTITLE }, - { 0xf8, 0x4a, KEY_LIST }, - { 0xf8, 0x46, KEY_F1 }, - { 0xf8, 0x47, KEY_F2 }, - { 0xf8, 0x5e, KEY_F3 }, - { 0xf8, 0x5c, KEY_F4 }, - { 0xf8, 0x52, KEY_F5 }, - { 0xf8, 0x5a, KEY_F6 }, - { 0xf8, 0x56, KEY_MODE }, - { 0xf8, 0x58, KEY_SWITCHVIDEOMODE }, + { 0xf80a, KEY_POWER }, + { 0xf80c, KEY_MUTE }, + { 0xf811, KEY_1 }, + { 0xf812, KEY_2 }, + { 0xf813, KEY_3 }, + { 0xf814, KEY_4 }, + { 0xf815, KEY_5 }, + { 0xf816, KEY_6 }, + { 0xf817, KEY_7 }, + { 0xf818, KEY_8 }, + { 0xf819, KEY_9 }, + { 0xf810, KEY_0 }, + { 0xf81c, KEY_MENU }, + { 0xf80f, KEY_VOLUMEDOWN }, + { 0xf81a, KEY_LAST }, + { 0xf80e, KEY_OPEN }, + { 0xf804, KEY_RECORD }, + { 0xf809, KEY_VOLUMEUP }, + { 0xf808, KEY_CHANNELUP }, + { 0xf807, KEY_PVR }, + { 0xf80b, KEY_TIME }, + { 0xf802, KEY_RIGHT }, + { 0xf803, KEY_LEFT }, + { 0xf800, KEY_UP }, + { 0xf81f, KEY_OK }, + { 0xf801, KEY_DOWN }, + { 0xf805, KEY_TUNER }, + { 0xf806, KEY_CHANNELDOWN }, + { 0xf840, KEY_PLAYPAUSE }, + { 0xf81e, KEY_REWIND }, + { 0xf81b, KEY_FAVORITES }, + { 0xf81d, KEY_BACK }, + { 0xf84d, KEY_FASTFORWARD }, + { 0xf844, KEY_EPG }, + { 0xf84c, KEY_INFO }, + { 0xf841, KEY_AB }, + { 0xf843, KEY_AUDIO }, + { 0xf845, KEY_SUBTITLE }, + { 0xf84a, KEY_LIST }, + { 0xf846, KEY_F1 }, + { 0xf847, KEY_F2 }, + { 0xf85e, KEY_F3 }, + { 0xf85c, KEY_F4 }, + { 0xf852, KEY_F5 }, + { 0xf85a, KEY_F6 }, + { 0xf856, KEY_MODE }, + { 0xf858, KEY_SWITCHVIDEOMODE }, }; static struct dvb_usb_rc_key tbs_rc_keys[] = { - { 0xf8, 0x84, KEY_POWER }, - { 0xf8, 0x94, KEY_MUTE }, - { 0xf8, 0x87, KEY_1 }, - { 0xf8, 0x86, KEY_2 }, - { 0xf8, 0x85, KEY_3 }, - { 0xf8, 0x8b, KEY_4 }, - { 0xf8, 0x8a, KEY_5 }, - { 0xf8, 0x89, KEY_6 }, - { 0xf8, 0x8f, KEY_7 }, - { 0xf8, 0x8e, KEY_8 }, - { 0xf8, 0x8d, KEY_9 }, - { 0xf8, 0x92, KEY_0 }, - { 0xf8, 0x96, KEY_CHANNELUP }, - { 0xf8, 0x91, KEY_CHANNELDOWN }, - { 0xf8, 0x93, KEY_VOLUMEUP }, - { 0xf8, 0x8c, KEY_VOLUMEDOWN }, - { 0xf8, 0x83, KEY_RECORD }, - { 0xf8, 0x98, KEY_PAUSE }, - { 0xf8, 0x99, KEY_OK }, - { 0xf8, 0x9a, KEY_SHUFFLE }, - { 0xf8, 0x81, KEY_UP }, - { 0xf8, 0x90, KEY_LEFT }, - { 0xf8, 0x82, KEY_RIGHT }, - { 0xf8, 0x88, KEY_DOWN }, - { 0xf8, 0x95, KEY_FAVORITES }, - { 0xf8, 0x97, KEY_SUBTITLE }, - { 0xf8, 0x9d, KEY_ZOOM }, - { 0xf8, 0x9f, KEY_EXIT }, - { 0xf8, 0x9e, KEY_MENU }, - { 0xf8, 0x9c, KEY_EPG }, - { 0xf8, 0x80, KEY_PREVIOUS }, - { 0xf8, 0x9b, KEY_MODE } + { 0xf884, KEY_POWER }, + { 0xf894, KEY_MUTE }, + { 0xf887, KEY_1 }, + { 0xf886, KEY_2 }, + { 0xf885, KEY_3 }, + { 0xf88b, KEY_4 }, + { 0xf88a, KEY_5 }, + { 0xf889, KEY_6 }, + { 0xf88f, KEY_7 }, + { 0xf88e, KEY_8 }, + { 0xf88d, KEY_9 }, + { 0xf892, KEY_0 }, + { 0xf896, KEY_CHANNELUP }, + { 0xf891, KEY_CHANNELDOWN }, + { 0xf893, KEY_VOLUMEUP }, + { 0xf88c, KEY_VOLUMEDOWN }, + { 0xf883, KEY_RECORD }, + { 0xf898, KEY_PAUSE }, + { 0xf899, KEY_OK }, + { 0xf89a, KEY_SHUFFLE }, + { 0xf881, KEY_UP }, + { 0xf890, KEY_LEFT }, + { 0xf882, KEY_RIGHT }, + { 0xf888, KEY_DOWN }, + { 0xf895, KEY_FAVORITES }, + { 0xf897, KEY_SUBTITLE }, + { 0xf89d, KEY_ZOOM }, + { 0xf89f, KEY_EXIT }, + { 0xf89e, KEY_MENU }, + { 0xf89c, KEY_EPG }, + { 0xf880, KEY_PREVIOUS }, + { 0xf89b, KEY_MODE } }; static struct dvb_usb_rc_keys_table keys_tables[] = { @@ -763,9 +892,9 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } *state = REMOTE_NO_KEY_PRESSED; - if (dw2102_i2c_transfer(&d->i2c_adap, &msg, 1) == 1) { + if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { for (i = 0; i < keymap_size ; i++) { - if (keymap[i].data == msg.buf[0]) { + if (rc5_data(&keymap[i]) == msg.buf[0]) { *state = REMOTE_KEY_PRESSED; *event = keymap[i].event; break; @@ -792,6 +921,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(0x9022, USB_PID_TEVII_S650)}, {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, + {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, { } }; @@ -806,6 +936,7 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; const char *filename = "dvb-usb-dw2101.fw"; + switch (dev->descriptor.idProduct) { case 0x2101: ret = request_firmware(&fw, filename, &dev->dev); @@ -1053,6 +1184,48 @@ static struct dvb_usb_device_properties dw3101_properties = { } }; +static struct dvb_usb_device_properties s630_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-s630.fw", + .no_reconnect = 1, + + .i2c_algo = &s630_i2c_algo, + .rc_key_map = tevii_rc_keys, + .rc_key_map_size = ARRAY_SIZE(tevii_rc_keys), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + + .generic_bulk_ctrl_endpoint = 0x81, + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = s630_read_mac_address, + .adapter = { + { + .frontend_attach = s630_frontend_attach, + .streaming_ctrl = NULL, + .tuner_attach = s630_zl10039_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 1, + .devices = { + {"TeVii S630 USB", + {&dw2102_table[6], NULL}, + {NULL}, + }, + } +}; + static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1061,6 +1234,8 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, &dw2104_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &dw3101_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &s630_properties, THIS_MODULE, NULL, adapter_nr)) { return 0; } @@ -1094,6 +1269,6 @@ module_exit(dw2102_module_exit); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," - " TeVii S600, S650 USB2.0 devices"); + " TeVii S600, S630, S650 USB2.0 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c new file mode 100644 index 00000000000..c4dfe25cf60 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -0,0 +1,483 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp> + * + * This module is based off the the gl861 and vp702x modules. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include "friio.h" + +struct jdvbt90502_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct jdvbt90502_config config; +}; + +/* NOTE: TC90502 has 16bit register-address? */ +/* register 0x0100 is used for reading PLL status, so reg is u16 here */ +static int jdvbt90502_reg_read(struct jdvbt90502_state *state, + const u16 reg, u8 *buf, const size_t count) +{ + int ret; + u8 wbuf[3]; + struct i2c_msg msg[2]; + + wbuf[0] = reg & 0xFF; + wbuf[1] = 0; + wbuf[2] = reg >> 8; + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = wbuf; + msg[0].len = sizeof(wbuf); + + msg[1].addr = msg[0].addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + deb_fe(" reg read failed.\n"); + return -EREMOTEIO; + } + return 0; +} + +/* currently 16bit register-address is not used, so reg is u8 here */ +static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state, + const u8 reg, const u8 val) +{ + struct i2c_msg msg; + u8 wbuf[2]; + + wbuf[0] = reg; + wbuf[1] = val; + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.buf = wbuf; + msg.len = sizeof(wbuf); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + deb_fe(" reg write failed."); + return -EREMOTEIO; + } + return 0; +} + +static int _jdvbt90502_write(struct dvb_frontend *fe, u8 *buf, int len) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + int err, i; + for (i = 0; i < len - 1; i++) { + err = jdvbt90502_single_reg_write(state, + buf[0] + i, buf[i + 1]); + if (err) + return err; + } + + return 0; +} + +/* read pll status byte via the demodulator's I2C register */ +/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */ +static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result) +{ + int ret; + + /* +1 for reading */ + u8 pll_addr_byte = (state->config.pll_address << 1) + 1; + + *result = 0; + + ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG, + pll_addr_byte); + if (ret) + goto error; + + ret = jdvbt90502_reg_read(state, 0x0100, result, 1); + if (ret) + goto error; + + deb_fe("PLL read val:%02x\n", *result); + return 0; + +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + + +/* set pll frequency via the demodulator's I2C register */ +static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) +{ + int ret; + int retry; + u8 res1; + u8 res2[9]; + + u8 pll_freq_cmd[PLL_CMD_LEN]; + u8 pll_agc_cmd[PLL_CMD_LEN]; + struct i2c_msg msg[2]; + u32 f; + + deb_fe("%s: freq=%d, step=%d\n", __func__, freq, + state->frontend.ops.info.frequency_stepsize); + /* freq -> oscilator frequency conversion. */ + /* freq: 473,000,000 + n*6,000,000 (no 1/7MHz shift to center freq) */ + /* add 400[1/7 MHZ] = 57.142857MHz. 57MHz for the IF, */ + /* 1/7MHz for center freq shift */ + f = freq / state->frontend.ops.info.frequency_stepsize; + f += 400; + pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */ + pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1; + pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F; + pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF; + pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */ + pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */ + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = pll_freq_cmd; + msg[0].len = sizeof(pll_freq_cmd); + + ret = i2c_transfer(state->i2c, &msg[0], 1); + if (ret != 1) + goto error; + + udelay(50); + + pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG]; + pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE]; + pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1]; + pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2]; + pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */ + pll_agc_cmd[AGC_CTRL_BYTE] = 0x50; + /* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */ + + msg[1].addr = msg[0].addr; + msg[1].flags = 0; + msg[1].buf = pll_agc_cmd; + msg[1].len = sizeof(pll_agc_cmd); + + ret = i2c_transfer(state->i2c, &msg[1], 1); + if (ret != 1) + goto error; + + /* I don't know what these cmds are for, */ + /* but the USB log on a windows box contains them */ + ret = jdvbt90502_single_reg_write(state, 0x01, 0x40); + ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00); + if (ret) + goto error; + udelay(100); + + /* wait for the demod to be ready? */ +#define RETRY_COUNT 5 + for (retry = 0; retry < RETRY_COUNT; retry++) { + ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1); + if (ret) + goto error; + /* if (res1 != 0x00) goto error; */ + ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2)); + if (ret) + goto error; + if (res2[0] >= 0xA7) + break; + msleep(100); + } + if (retry >= RETRY_COUNT) { + deb_fe("%s: FE does not get ready after freq setting.\n", + __func__); + return -EREMOTEIO; + } + + return 0; +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + +static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) +{ + u8 result; + int ret; + + *state = FE_HAS_SIGNAL; + + ret = jdvbt90502_pll_read(fe->demodulator_priv, &result); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + *state = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC; + + if (result & PLL_STATUS_LOCKED) + *state |= FE_HAS_LOCK; + + return 0; +} + +static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + int ret; + u8 rbuf[37]; + + *strength = 0; + + /* status register (incl. signal strength) : 0x89 */ + /* TODO: read just the necessary registers [0x8B..0x8D]? */ + ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089, + rbuf, sizeof(rbuf)); + + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + /* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */ + *strength = (rbuf[3] << 8) + rbuf[4]; + if (rbuf[2]) + *strength = 0xffff; + + return 0; +} + +static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0x0101; + return 0; +} + +static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fs) +{ + fs->min_delay_ms = 500; + fs->step_size = 0; + fs->max_drift = 0; + + return 0; +} + +static int jdvbt90502_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + p->inversion = INVERSION_AUTO; + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + p->u.ofdm.code_rate_HP = FEC_AUTO; + p->u.ofdm.code_rate_LP = FEC_AUTO; + p->u.ofdm.constellation = QAM_64; + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p->u.ofdm.hierarchy_information = HIERARCHY_AUTO; + return 0; +} + +static int jdvbt90502_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + /** + * NOTE: ignore all the paramters except frequency. + * others should be fixed to the proper value for ISDB-T, + * but don't check here. + */ + + struct jdvbt90502_state *state = fe->demodulator_priv; + int ret; + + deb_fe("%s: Freq:%d\n", __func__, p->frequency); + + ret = jdvbt90502_pll_set_freq(state, p->frequency); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + return 0; +} + +static int jdvbt90502_sleep(struct dvb_frontend *fe) +{ + deb_fe("%s called.\n", __func__); + return 0; +} + + +/** + * (reg, val) commad list to initialize this module. + * captured on a Windows box. + */ +static u8 init_code[][2] = { + {0x01, 0x40}, + {0x04, 0x38}, + {0x05, 0x40}, + {0x07, 0x40}, + {0x0F, 0x4F}, + {0x11, 0x21}, + {0x12, 0x0B}, + {0x13, 0x2F}, + {0x14, 0x31}, + {0x16, 0x02}, + {0x21, 0xC4}, + {0x22, 0x20}, + {0x2C, 0x79}, + {0x2D, 0x34}, + {0x2F, 0x00}, + {0x30, 0x28}, + {0x31, 0x31}, + {0x32, 0xDF}, + {0x38, 0x01}, + {0x39, 0x78}, + {0x3B, 0x33}, + {0x3C, 0x33}, + {0x48, 0x90}, + {0x51, 0x68}, + {0x5E, 0x38}, + {0x71, 0x00}, + {0x72, 0x08}, + {0x77, 0x00}, + {0xC0, 0x21}, + {0xC1, 0x10}, + {0xE4, 0x1A}, + {0xEA, 0x1F}, + {0x77, 0x00}, + {0x71, 0x00}, + {0x71, 0x00}, + {0x76, 0x0C}, +}; + +const static int init_code_len = sizeof(init_code) / sizeof(u8[2]); + +static int jdvbt90502_init(struct dvb_frontend *fe) +{ + int i = -1; + int ret; + struct i2c_msg msg; + + struct jdvbt90502_state *state = fe->demodulator_priv; + + deb_fe("%s called.\n", __func__); + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < init_code_len; i++) { + msg.buf = init_code[i]; + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + goto error; + } + msleep(100); + + return 0; + +error: + deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret); + return -EREMOTEIO; +} + + +static void jdvbt90502_release(struct dvb_frontend *fe) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + kfree(state); +} + + +static struct dvb_frontend_ops jdvbt90502_ops; + +struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) +{ + struct jdvbt90502_state *state = NULL; + + deb_info("%s called.\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = &d->i2c_adap; + memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &jdvbt90502_ops, + sizeof(jdvbt90502_ops)); + state->frontend.demodulator_priv = state; + + if (jdvbt90502_init(&state->frontend) < 0) + goto error; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops jdvbt90502_ops = { + + .info = { + .name = "Comtech JDVBT90502 ISDB-T", + .type = FE_OFDM, + .frequency_min = 473000000, /* UHF 13ch, center */ + .frequency_max = 767142857, /* UHF 62ch, center */ + .frequency_stepsize = JDVBT90502_PLL_CLK / + JDVBT90502_PLL_DIVIDER, + .frequency_tolerance = 0, + + /* NOTE: this driver ignores all parameters but frequency. */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = jdvbt90502_release, + + .init = jdvbt90502_init, + .sleep = jdvbt90502_sleep, + .write = _jdvbt90502_write, + + .set_frontend = jdvbt90502_set_frontend, + .get_frontend = jdvbt90502_get_frontend, + .get_tune_settings = jdvbt90502_get_tune_settings, + + .read_status = jdvbt90502_read_status, + .read_ber = jdvbt90502_read_ber, + .read_signal_strength = jdvbt90502_read_signal_strength, + .read_snr = jdvbt90502_read_snr, + .read_ucblocks = jdvbt90502_read_ucblocks, +}; diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c new file mode 100644 index 00000000000..14a65b4aec0 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio.c @@ -0,0 +1,525 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp> + * + * This module is based off the the gl861 and vp702x modules. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "friio.h" + +/* debug */ +int dvb_usb_friio_debug; +module_param_named(debug, dvb_usb_friio_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/** + * Indirect I2C access to the PLL via FE. + * whole I2C protocol data to the PLL is sent via the FE's I2C register. + * This is done by a control msg to the FE with the I2C data accompanied, and + * a specific USB request number is assigned for that purpose. + * + * this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE). + * TODO: refoctored, smarter i2c functions. + */ +static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */ + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write only */ + u8 req, type; + + deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n", + wbuf[1], wbuf[0], wlen - 1); + + if (wo && wlen >= 2) { + req = GL861_REQ_I2C_DATA_CTRL_WRITE; + type = GL861_WRITE; + udelay(20); + return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + req, type, value, index, + &wbuf[1], wlen - 1, 2000); + } + + deb_xfer("not supported ctrl-msg, aborting."); + return -EINVAL; +} + +/* normal I2C access (without extra data arguments). + * write to the register wbuf[0] at I2C address addr with the value wbuf[1], + * or read from the register wbuf[0]. + * register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3 + */ +static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index; + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 req, type; + unsigned int pipe; + + /* special case for the indirect I2C access to the PLL via FE, */ + if (addr == friio_fe_config.demod_address && + wbuf[0] == JDVBT90502_2ND_I2C_REG) + return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen); + + if (wo) { + req = GL861_REQ_I2C_WRITE; + type = GL861_WRITE; + pipe = usb_sndctrlpipe(d->udev, 0); + } else { /* rw */ + req = GL861_REQ_I2C_READ; + type = GL861_READ; + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + switch (wlen) { + case 1: + index = wbuf[0]; + break; + case 2: + index = wbuf[0]; + value = value + wbuf[1]; + break; + case 3: + /* special case for 16bit register-address */ + index = (wbuf[2] << 8) | wbuf[0]; + value = value + wbuf[1]; + break; + default: + deb_xfer("wlen = %x, aborting.", wlen); + return -EINVAL; + } + msleep(1); + return usb_control_msg(d->udev, pipe, req, type, + value, index, rbuf, rlen, 2000); +} + +/* I2C */ +static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { + if (gl861_i2c_msg(d, msg[i].addr, + msg[i].buf, msg[i].len, + msg[i + 1].buf, msg[i + 1].len) < 0) + break; + i++; + } else + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 gl861_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + + +static int friio_ext_ctl(struct dvb_usb_adapter *adap, + u32 sat_color, int lnb_on) +{ + int i; + int ret; + struct i2c_msg msg; + u8 buf[2]; + u32 mask; + u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0; + + msg.addr = 0x00; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + + buf[0] = 0x00; + + /* send 2bit header (&B10) */ + buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE; + ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + buf[1] = lnb | FRIIO_CTL_STROBE; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + /* send 32bit(satur, R, G, B) data in serial */ + mask = 1 << 31; + for (i = 0; i < 32; i++) { + buf[1] = lnb | FRIIO_CTL_STROBE; + if (sat_color & mask) + buf[1] |= FRIIO_CTL_LED; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + mask >>= 1; + } + + /* set the strobe off */ + buf[1] = lnb; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + return (ret == 70); +} + + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); + +/* TODO: move these init cmds to the FE's init routine? */ +static u8 streaming_init_cmds[][2] = { + {0x33, 0x08}, + {0x37, 0x40}, + {0x3A, 0x1F}, + {0x3B, 0xFF}, + {0x3C, 0x1F}, + {0x3D, 0xFF}, + {0x38, 0x00}, + {0x35, 0x00}, + {0x39, 0x00}, + {0x36, 0x00}, +}; +static int cmdlen = sizeof(streaming_init_cmds) / 2; + +/* + * Command sequence in this init function is a replay + * of the captured USB commands from the Windows proprietary driver. + */ +static int friio_initialize(struct dvb_usb_device *d) +{ + int ret; + int i; + int retry = 0; + u8 rbuf[2]; + u8 wbuf[3]; + + deb_info("%s called.\n", __func__); + + /* use gl861_i2c_msg instead of gl861_i2c_xfer(), */ + /* because the i2c device is not set up yet. */ + wbuf[0] = 0x11; + wbuf[1] = 0x02; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(2); + + wbuf[0] = 0x11; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(1); + + /* following msgs should be in the FE's init code? */ + /* cmd sequence to identify the device type? (friio black/white) */ + wbuf[0] = 0x03; + wbuf[1] = 0x80; + /* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */ + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x1200, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(2); + wbuf[0] = 0x03; + wbuf[1] = 0x80; + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x9000, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff again. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(1); + +restart: + /* ============ start DEMOD init cmds ================== */ + /* read PLL status to clear the POR bit */ + wbuf[0] = JDVBT90502_2ND_I2C_REG; + wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(5); + /* note: DEMODULATOR has 16bit register-address. */ + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg addr: 0x0100 */ + wbuf[1] = 0x00; /* val: not used */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1); + if (ret < 0) + goto error; +/* + msleep(1); + wbuf[0] = 0x80; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1); + if (ret < 0) + goto error; + */ + if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */ + if (++retry > 3) { + deb_info("failed to get the correct" + " FE demod status:0x%02x\n", rbuf[0]); + goto error; + } + msleep(100); + goto restart; + } + + /* TODO: check return value in rbuf */ + /* =========== end DEMOD init cmds ===================== */ + msleep(1); + + wbuf[0] = 0x30; + wbuf[1] = 0x04; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(2); + /* following 2 cmds unnecessary? */ + wbuf[0] = 0x00; + wbuf[1] = 0x01; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + wbuf[0] = 0x06; + wbuf[1] = 0x0F; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + /* some streaming ctl cmds (maybe) */ + msleep(10); + for (i = 0; i < cmdlen; i++) { + ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2, + NULL, 0); + if (ret < 0) + goto error; + msleep(1); + } + msleep(20); + + /* change the LED color etc. */ + ret = friio_streaming_ctrl(&d->adapter[0], 0); + if (ret < 0) + goto error; + + return 0; + +error: + deb_info("%s:ret == %d\n", __func__, ret); + return -EIO; +} + +/* Callbacks for DVB USB */ + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + + deb_info("%s called.(%d)\n", __func__, onoff); + + /* set the LED color and saturation (and LNB on) */ + if (onoff) + ret = friio_ext_ctl(adap, 0x6400ff64, 1); + else + ret = friio_ext_ctl(adap, 0x96ff00ff, 1); + + if (ret != 1) { + deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret); + return -EREMOTEIO; + } + return 0; +} + +static int friio_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (friio_initialize(adap->dev) < 0) + return -EIO; + + adap->fe = jdvbt90502_attach(adap->dev); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties friio_properties; + +static int friio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + if (intf->num_altsetting < GL861_ALTSETTING_COUNT) + return -ENODEV; + + alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING); + if (alt == NULL) { + deb_rc("not alt found!\n"); + return -ENODEV; + } + ret = usb_set_interface(interface_to_usbdev(intf), + alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret != 0) { + deb_rc("failed to set alt-setting!\n"); + return ret; + } + + ret = dvb_usb_device_init(intf, &friio_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) + friio_streaming_ctrl(&d->adapter[0], 1); + + return ret; +} + + +struct jdvbt90502_config friio_fe_config = { + .demod_address = FRIIO_DEMOD_ADDR, + .pll_address = FRIIO_PLL_ADDR, +}; + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +static struct usb_device_id friio_table[] = { + { USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, friio_table); + + +static struct dvb_usb_device_properties friio_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = { + /* caps:0 => no pid filter, 188B TS packet */ + /* GL861 has a HW pid filter, but no info available. */ + { + .caps = 0, + + .frontend_attach = friio_frontend_attach, + .streaming_ctrl = friio_streaming_ctrl, + + .stream = { + .type = USB_BULK, + /* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */ + .count = 8, + .endpoint = 0x01, + .u = { + /* GL861 has 6KB buf inside */ + .bulk = { + .buffersize = 16384, + } + } + }, + } + }, + .i2c_algo = &gl861_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "774 Friio ISDB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &friio_table[0], NULL }, + }, + } +}; + +static struct usb_driver friio_driver = { + .name = "dvb_usb_friio", + .probe = friio_probe, + .disconnect = dvb_usb_device_exit, + .id_table = friio_table, +}; + + +/* module stuff */ +static int __init friio_module_init(void) +{ + int ret; + + ret = usb_register(&friio_driver); + if (ret) + err("usb_register failed. Error number %d", ret); + + return ret; +} + + +static void __exit friio_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&friio_driver); +} + +module_init(friio_module_init); +module_exit(friio_module_exit); + +MODULE_AUTHOR("Akihiro Tsukada <tskd2@yahoo.co.jp>"); +MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver"); +MODULE_VERSION("0.2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/friio.h b/drivers/media/dvb/dvb-usb/friio.h new file mode 100644 index 00000000000..af8d55e390f --- /dev/null +++ b/drivers/media/dvb/dvb-usb/friio.h @@ -0,0 +1,99 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp> + * + * This module is based off the the gl861 and vp702x modules. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_FRIIO_H_ +#define _DVB_USB_FRIIO_H_ + +/** + * Friio Components + * USB hub: AU4254 + * USB controller(+ TS dmx & streaming): GL861 + * Frontend: comtech JDVBT-90502 + * (tuner PLL: tua6034, I2C addr:(0xC0 >> 1)) + * (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1)) + * LED x3 (+LNB) controll: PIC 16F676 + * EEPROM: 24C08 + * + * (USB smart card reader: AU9522) + * + */ + +#define DVB_USB_LOG_PREFIX "friio" +#include "dvb-usb.h" + +extern int dvb_usb_friio_debug; +#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args) + +/* Vendor requests */ +#define GL861_WRITE 0x40 +#define GL861_READ 0xc0 + +/* command bytes */ +#define GL861_REQ_I2C_WRITE 0x01 +#define GL861_REQ_I2C_READ 0x02 +/* For control msg with data argument */ +/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */ +#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03 + +#define GL861_ALTSETTING_COUNT 2 +#define FRIIO_BULK_ALTSETTING 0 +#define FRIIO_ISOC_ALTSETTING 1 + +/* LED & LNB control via PIC. */ +/* basically, it's serial control with clock and strobe. */ +/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */ +/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/ +#define FRIIO_CTL_LNB (1 << 0) +#define FRIIO_CTL_STROBE (1 << 1) +#define FRIIO_CTL_CLK (1 << 2) +#define FRIIO_CTL_LED (1 << 3) + +/* Front End related */ + +#define FRIIO_DEMOD_ADDR (0x30 >> 1) +#define FRIIO_PLL_ADDR (0xC0 >> 1) + +#define JDVBT90502_PLL_CLK 4000000 +#define JDVBT90502_PLL_DIVIDER 28 + +#define JDVBT90502_2ND_I2C_REG 0xFE + +/* byte index for pll i2c command data structure*/ +/* see datasheet for tua6034 */ +#define DEMOD_REDIRECT_REG 0 +#define ADDRESS_BYTE 1 +#define DIVIDER_BYTE1 2 +#define DIVIDER_BYTE2 3 +#define CONTROL_BYTE 4 +#define BANDSWITCH_BYTE 5 +#define AGC_CTRL_BYTE 5 +#define PLL_CMD_LEN 6 + +/* bit masks for PLL STATUS response */ +#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */ +#define PLL_STATUS_LOCKED 0x40 /* 1: locked */ +#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */ +#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */ + /* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */ + + +struct jdvbt90502_config { + u8 demod_address; /* i2c addr for demodulator IC */ + u8 pll_address; /* PLL addr on the secondary i2c*/ +}; +extern struct jdvbt90502_config friio_fe_config; + +extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d); +#endif diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index 54626a0dbf6..ef9b7bed13f 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -140,7 +140,7 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) goto unlock; for (i = 0; i < d->props.rc_key_map_size; i++) - if (d->props.rc_key_map[i].data == rc_state[1]) { + if (rc5_data(&d->props.rc_key_map[i]) == rc_state[1]) { *event = d->props.rc_key_map[i].event; switch(rc_state[0]) { @@ -337,6 +337,8 @@ static int m920x_firmware_download(struct usb_device *udev, const struct firmwar int i, pass, ret = 0; buff = kmalloc(65536, GFP_KERNEL); + if (buff == NULL) + return -ENOMEM; if ((ret = m920x_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0) goto done; @@ -562,42 +564,42 @@ static struct m920x_inits tvwalkertwin_rc_init [] = { /* ir keymaps */ static struct dvb_usb_rc_key megasky_rc_keys [] = { - { 0x0, 0x12, KEY_POWER }, - { 0x0, 0x1e, KEY_CYCLEWINDOWS }, /* min/max */ - { 0x0, 0x02, KEY_CHANNELUP }, - { 0x0, 0x05, KEY_CHANNELDOWN }, - { 0x0, 0x03, KEY_VOLUMEUP }, - { 0x0, 0x06, KEY_VOLUMEDOWN }, - { 0x0, 0x04, KEY_MUTE }, - { 0x0, 0x07, KEY_OK }, /* TS */ - { 0x0, 0x08, KEY_STOP }, - { 0x0, 0x09, KEY_MENU }, /* swap */ - { 0x0, 0x0a, KEY_REWIND }, - { 0x0, 0x1b, KEY_PAUSE }, - { 0x0, 0x1f, KEY_FASTFORWARD }, - { 0x0, 0x0c, KEY_RECORD }, - { 0x0, 0x0d, KEY_CAMERA }, /* screenshot */ - { 0x0, 0x0e, KEY_COFFEE }, /* "MTS" */ + { 0x0012, KEY_POWER }, + { 0x001e, KEY_CYCLEWINDOWS }, /* min/max */ + { 0x0002, KEY_CHANNELUP }, + { 0x0005, KEY_CHANNELDOWN }, + { 0x0003, KEY_VOLUMEUP }, + { 0x0006, KEY_VOLUMEDOWN }, + { 0x0004, KEY_MUTE }, + { 0x0007, KEY_OK }, /* TS */ + { 0x0008, KEY_STOP }, + { 0x0009, KEY_MENU }, /* swap */ + { 0x000a, KEY_REWIND }, + { 0x001b, KEY_PAUSE }, + { 0x001f, KEY_FASTFORWARD }, + { 0x000c, KEY_RECORD }, + { 0x000d, KEY_CAMERA }, /* screenshot */ + { 0x000e, KEY_COFFEE }, /* "MTS" */ }; static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = { - { 0x0, 0x01, KEY_ZOOM }, /* Full Screen */ - { 0x0, 0x02, KEY_CAMERA }, /* snapshot */ - { 0x0, 0x03, KEY_MUTE }, - { 0x0, 0x04, KEY_REWIND }, - { 0x0, 0x05, KEY_PLAYPAUSE }, /* Play/Pause */ - { 0x0, 0x06, KEY_FASTFORWARD }, - { 0x0, 0x07, KEY_RECORD }, - { 0x0, 0x08, KEY_STOP }, - { 0x0, 0x09, KEY_TIME }, /* Timeshift */ - { 0x0, 0x0c, KEY_COFFEE }, /* Recall */ - { 0x0, 0x0e, KEY_CHANNELUP }, - { 0x0, 0x12, KEY_POWER }, - { 0x0, 0x15, KEY_MENU }, /* source */ - { 0x0, 0x18, KEY_CYCLEWINDOWS }, /* TWIN PIP */ - { 0x0, 0x1a, KEY_CHANNELDOWN }, - { 0x0, 0x1b, KEY_VOLUMEDOWN }, - { 0x0, 0x1e, KEY_VOLUMEUP }, + { 0x0001, KEY_ZOOM }, /* Full Screen */ + { 0x0002, KEY_CAMERA }, /* snapshot */ + { 0x0003, KEY_MUTE }, + { 0x0004, KEY_REWIND }, + { 0x0005, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x0006, KEY_FASTFORWARD }, + { 0x0007, KEY_RECORD }, + { 0x0008, KEY_STOP }, + { 0x0009, KEY_TIME }, /* Timeshift */ + { 0x000c, KEY_COFFEE }, /* Recall */ + { 0x000e, KEY_CHANNELUP }, + { 0x0012, KEY_POWER }, + { 0x0015, KEY_MENU }, /* source */ + { 0x0018, KEY_CYCLEWINDOWS }, /* TWIN PIP */ + { 0x001a, KEY_CHANNELDOWN }, + { 0x001b, KEY_VOLUMEDOWN }, + { 0x001e, KEY_VOLUMEUP }, }; /* DVB USB Driver stuff */ diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index 07fb843c7c2..b41d66ef832 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -22,51 +22,51 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* Hauppauge NOVA-T USB2 keys */ static struct dvb_usb_rc_key haupp_rc_keys [] = { - { 0x1e, 0x00, KEY_0 }, - { 0x1e, 0x01, KEY_1 }, - { 0x1e, 0x02, KEY_2 }, - { 0x1e, 0x03, KEY_3 }, - { 0x1e, 0x04, KEY_4 }, - { 0x1e, 0x05, KEY_5 }, - { 0x1e, 0x06, KEY_6 }, - { 0x1e, 0x07, KEY_7 }, - { 0x1e, 0x08, KEY_8 }, - { 0x1e, 0x09, KEY_9 }, - { 0x1e, 0x0a, KEY_KPASTERISK }, - { 0x1e, 0x0b, KEY_RED }, - { 0x1e, 0x0c, KEY_RADIO }, - { 0x1e, 0x0d, KEY_MENU }, - { 0x1e, 0x0e, KEY_GRAVE }, /* # */ - { 0x1e, 0x0f, KEY_MUTE }, - { 0x1e, 0x10, KEY_VOLUMEUP }, - { 0x1e, 0x11, KEY_VOLUMEDOWN }, - { 0x1e, 0x12, KEY_CHANNEL }, - { 0x1e, 0x14, KEY_UP }, - { 0x1e, 0x15, KEY_DOWN }, - { 0x1e, 0x16, KEY_LEFT }, - { 0x1e, 0x17, KEY_RIGHT }, - { 0x1e, 0x18, KEY_VIDEO }, - { 0x1e, 0x19, KEY_AUDIO }, - { 0x1e, 0x1a, KEY_MEDIA }, - { 0x1e, 0x1b, KEY_EPG }, - { 0x1e, 0x1c, KEY_TV }, - { 0x1e, 0x1e, KEY_NEXT }, - { 0x1e, 0x1f, KEY_BACK }, - { 0x1e, 0x20, KEY_CHANNELUP }, - { 0x1e, 0x21, KEY_CHANNELDOWN }, - { 0x1e, 0x24, KEY_LAST }, /* Skip backwards */ - { 0x1e, 0x25, KEY_OK }, - { 0x1e, 0x29, KEY_BLUE}, - { 0x1e, 0x2e, KEY_GREEN }, - { 0x1e, 0x30, KEY_PAUSE }, - { 0x1e, 0x32, KEY_REWIND }, - { 0x1e, 0x34, KEY_FASTFORWARD }, - { 0x1e, 0x35, KEY_PLAY }, - { 0x1e, 0x36, KEY_STOP }, - { 0x1e, 0x37, KEY_RECORD }, - { 0x1e, 0x38, KEY_YELLOW }, - { 0x1e, 0x3b, KEY_GOTO }, - { 0x1e, 0x3d, KEY_POWER }, + { 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_KPASTERISK }, + { 0x1e0b, KEY_RED }, + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_GRAVE }, /* # */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_CHANNEL }, + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, + { 0x1e19, KEY_AUDIO }, + { 0x1e1a, KEY_MEDIA }, + { 0x1e1b, KEY_EPG }, + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXT }, + { 0x1e1f, KEY_BACK }, + { 0x1e20, KEY_CHANNELUP }, + { 0x1e21, KEY_CHANNELDOWN }, + { 0x1e24, KEY_LAST }, /* Skip backwards */ + { 0x1e25, KEY_OK }, + { 0x1e29, KEY_BLUE}, + { 0x1e2e, KEY_GREEN }, + { 0x1e30, KEY_PAUSE }, + { 0x1e32, KEY_REWIND }, + { 0x1e34, KEY_FASTFORWARD }, + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, + { 0x1e38, KEY_YELLOW }, + { 0x1e3b, KEY_GOTO }, + { 0x1e3d, KEY_POWER }, }; /* Firmware bug? sometimes, when a new key is pressed, the previous pressed key @@ -92,10 +92,11 @@ static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle); for (i = 0; i < ARRAY_SIZE(haupp_rc_keys); i++) { - if (haupp_rc_keys[i].data == data && - haupp_rc_keys[i].custom == custom) { + if (rc5_data(&haupp_rc_keys[i]) == data && + rc5_custom(&haupp_rc_keys[i]) == custom) { - deb_rc("c: %x, d: %x\n",haupp_rc_keys[i].data,haupp_rc_keys[i].custom); + deb_rc("c: %x, d: %x\n", rc5_data(&haupp_rc_keys[i]), + rc5_custom(&haupp_rc_keys[i])); *event = haupp_rc_keys[i].event; *state = REMOTE_KEY_PRESSED; diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index 7e32d11f32b..d4e23094167 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -332,32 +332,32 @@ static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) } static struct dvb_usb_rc_key opera1_rc_keys[] = { - {0x5f, 0xa0, KEY_1}, - {0x51, 0xaf, KEY_2}, - {0x5d, 0xa2, KEY_3}, - {0x41, 0xbe, KEY_4}, - {0x0b, 0xf5, KEY_5}, - {0x43, 0xbd, KEY_6}, - {0x47, 0xb8, KEY_7}, - {0x49, 0xb6, KEY_8}, - {0x05, 0xfa, KEY_9}, - {0x45, 0xba, KEY_0}, - {0x09, 0xf6, KEY_UP}, /*chanup */ - {0x1b, 0xe5, KEY_DOWN}, /*chandown */ - {0x5d, 0xa3, KEY_LEFT}, /*voldown */ - {0x5f, 0xa1, KEY_RIGHT}, /*volup */ - {0x07, 0xf8, KEY_SPACE}, /*tab */ - {0x1f, 0xe1, KEY_ENTER}, /*play ok */ - {0x1b, 0xe4, KEY_Z}, /*zoom */ - {0x59, 0xa6, KEY_M}, /*mute */ - {0x5b, 0xa5, KEY_F}, /*tv/f */ - {0x19, 0xe7, KEY_R}, /*rec */ - {0x01, 0xfe, KEY_S}, /*Stop */ - {0x03, 0xfd, KEY_P}, /*pause */ - {0x03, 0xfc, KEY_W}, /*<- -> */ - {0x07, 0xf9, KEY_C}, /*capture */ - {0x47, 0xb9, KEY_Q}, /*exit */ - {0x43, 0xbc, KEY_O}, /*power */ + {0x5fa0, KEY_1}, + {0x51af, KEY_2}, + {0x5da2, KEY_3}, + {0x41be, KEY_4}, + {0x0bf5, KEY_5}, + {0x43bd, KEY_6}, + {0x47b8, KEY_7}, + {0x49b6, KEY_8}, + {0x05fa, KEY_9}, + {0x45ba, KEY_0}, + {0x09f6, KEY_UP}, /*chanup */ + {0x1be5, KEY_DOWN}, /*chandown */ + {0x5da3, KEY_LEFT}, /*voldown */ + {0x5fa1, KEY_RIGHT}, /*volup */ + {0x07f8, KEY_SPACE}, /*tab */ + {0x1fe1, KEY_ENTER}, /*play ok */ + {0x1be4, KEY_Z}, /*zoom */ + {0x59a6, KEY_M}, /*mute */ + {0x5ba5, KEY_F}, /*tv/f */ + {0x19e7, KEY_R}, /*rec */ + {0x01fe, KEY_S}, /*Stop */ + {0x03fd, KEY_P}, /*pause */ + {0x03fc, KEY_W}, /*<- -> */ + {0x07f9, KEY_C}, /*capture */ + {0x47b9, KEY_Q}, /*exit */ + {0x43bc, KEY_O}, /*power */ }; @@ -405,8 +405,7 @@ static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) send_key = (send_key & 0xffff) | 0x0100; for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) { - if ((opera1_rc_keys[i].custom * 256 + - opera1_rc_keys[i].data) == (send_key & 0xffff)) { + if (rc5_scan(&opera1_rc_keys[i]) == (send_key & 0xffff)) { *state = REMOTE_KEY_PRESSED; *event = opera1_rc_keys[i].event; opst->last_key_pressed = diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index 986fff9a5ba..ef4e37d9c5f 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -175,8 +175,8 @@ static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) /* keys for the enclosed remote control */ static struct dvb_usb_rc_key vp702x_rc_keys[] = { - { 0x00, 0x01, KEY_1 }, - { 0x00, 0x02, KEY_2 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, }; /* remote control stuff (does not work with my box) */ @@ -198,7 +198,7 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++) - if (vp702x_rc_keys[i].custom == key[1]) { + if (rc5_custom(&vp702x_rc_keys[i]) == key[1]) { *state = REMOTE_KEY_PRESSED; *event = vp702x_rc_keys[i].event; break; diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index acb345504e0..a59faa27912 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -100,56 +100,56 @@ static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) /* The keymapping struct. Somehow this should be loaded to the driver, but * currently it is hardcoded. */ static struct dvb_usb_rc_key vp7045_rc_keys[] = { - { 0x00, 0x16, KEY_POWER }, - { 0x00, 0x10, KEY_MUTE }, - { 0x00, 0x03, KEY_1 }, - { 0x00, 0x01, KEY_2 }, - { 0x00, 0x06, KEY_3 }, - { 0x00, 0x09, KEY_4 }, - { 0x00, 0x1d, KEY_5 }, - { 0x00, 0x1f, KEY_6 }, - { 0x00, 0x0d, KEY_7 }, - { 0x00, 0x19, KEY_8 }, - { 0x00, 0x1b, KEY_9 }, - { 0x00, 0x15, KEY_0 }, - { 0x00, 0x05, KEY_CHANNELUP }, - { 0x00, 0x02, KEY_CHANNELDOWN }, - { 0x00, 0x1e, KEY_VOLUMEUP }, - { 0x00, 0x0a, KEY_VOLUMEDOWN }, - { 0x00, 0x11, KEY_RECORD }, - { 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ - { 0x00, 0x14, KEY_PLAY }, - { 0x00, 0x1a, KEY_STOP }, - { 0x00, 0x40, KEY_REWIND }, - { 0x00, 0x12, KEY_FASTFORWARD }, - { 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ - { 0x00, 0x4c, KEY_PAUSE }, - { 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */ - { 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ - { 0x00, 0x0c, KEY_CANCEL }, /* Cancel */ - { 0x00, 0x1c, KEY_EPG }, /* EPG */ - { 0x00, 0x00, KEY_TAB }, /* Tab */ - { 0x00, 0x48, KEY_INFO }, /* Preview */ - { 0x00, 0x04, KEY_LIST }, /* RecordList */ - { 0x00, 0x0f, KEY_TEXT }, /* Teletext */ - { 0x00, 0x41, KEY_PREVIOUSSONG }, - { 0x00, 0x42, KEY_NEXTSONG }, - { 0x00, 0x4b, KEY_UP }, - { 0x00, 0x51, KEY_DOWN }, - { 0x00, 0x4e, KEY_LEFT }, - { 0x00, 0x52, KEY_RIGHT }, - { 0x00, 0x4f, KEY_ENTER }, - { 0x00, 0x13, KEY_CANCEL }, - { 0x00, 0x4a, KEY_CLEAR }, - { 0x00, 0x54, KEY_PRINT }, /* Capture */ - { 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */ - { 0x00, 0x08, KEY_VIDEO }, /* A/V */ - { 0x00, 0x07, KEY_SLEEP }, /* Hibernate */ - { 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */ - { 0x00, 0x18, KEY_RED}, - { 0x00, 0x53, KEY_GREEN}, - { 0x00, 0x5e, KEY_YELLOW}, - { 0x00, 0x5f, KEY_BLUE} + { 0x0016, KEY_POWER }, + { 0x0010, KEY_MUTE }, + { 0x0003, KEY_1 }, + { 0x0001, KEY_2 }, + { 0x0006, KEY_3 }, + { 0x0009, KEY_4 }, + { 0x001d, KEY_5 }, + { 0x001f, KEY_6 }, + { 0x000d, KEY_7 }, + { 0x0019, KEY_8 }, + { 0x001b, KEY_9 }, + { 0x0015, KEY_0 }, + { 0x0005, KEY_CHANNELUP }, + { 0x0002, KEY_CHANNELDOWN }, + { 0x001e, KEY_VOLUMEUP }, + { 0x000a, KEY_VOLUMEDOWN }, + { 0x0011, KEY_RECORD }, + { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x0014, KEY_PLAY }, + { 0x001a, KEY_STOP }, + { 0x0040, KEY_REWIND }, + { 0x0012, KEY_FASTFORWARD }, + { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x004c, KEY_PAUSE }, + { 0x004d, KEY_SCREEN }, /* Full screen mode. */ + { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + { 0x000c, KEY_CANCEL }, /* Cancel */ + { 0x001c, KEY_EPG }, /* EPG */ + { 0x0000, KEY_TAB }, /* Tab */ + { 0x0048, KEY_INFO }, /* Preview */ + { 0x0004, KEY_LIST }, /* RecordList */ + { 0x000f, KEY_TEXT }, /* Teletext */ + { 0x0041, KEY_PREVIOUSSONG }, + { 0x0042, KEY_NEXTSONG }, + { 0x004b, KEY_UP }, + { 0x0051, KEY_DOWN }, + { 0x004e, KEY_LEFT }, + { 0x0052, KEY_RIGHT }, + { 0x004f, KEY_ENTER }, + { 0x0013, KEY_CANCEL }, + { 0x004a, KEY_CLEAR }, + { 0x0054, KEY_PRINT }, /* Capture */ + { 0x0043, KEY_SUBTITLE }, /* Subtitle/CC */ + { 0x0008, KEY_VIDEO }, /* A/V */ + { 0x0007, KEY_SLEEP }, /* Hibernate */ + { 0x0045, KEY_ZOOM }, /* Zoom+ */ + { 0x0018, KEY_RED}, + { 0x0053, KEY_GREEN}, + { 0x005e, KEY_YELLOW}, + { 0x005f, KEY_BLUE} }; static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) @@ -166,7 +166,7 @@ static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } for (i = 0; i < ARRAY_SIZE(vp7045_rc_keys); i++) - if (vp7045_rc_keys[i].data == key) { + if (rc5_data(&vp7045_rc_keys[i]) == key) { *state = REMOTE_KEY_PRESSED; *event = vp7045_rc_keys[i].event; break; diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 32526f103b5..d1b67fe0f01 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -89,15 +89,33 @@ struct avc_response_frame { u8 operand[509]; }; -#define AVC_DEBUG_FCP_SUBACTIONS 1 -#define AVC_DEBUG_FCP_PAYLOADS 2 +#define AVC_DEBUG_READ_DESCRIPTOR 0x0001 +#define AVC_DEBUG_DSIT 0x0002 +#define AVC_DEBUG_DSD 0x0004 +#define AVC_DEBUG_REGISTER_REMOTE_CONTROL 0x0008 +#define AVC_DEBUG_LNB_CONTROL 0x0010 +#define AVC_DEBUG_TUNE_QPSK 0x0020 +#define AVC_DEBUG_TUNE_QPSK2 0x0040 +#define AVC_DEBUG_HOST2CA 0x0080 +#define AVC_DEBUG_CA2HOST 0x0100 +#define AVC_DEBUG_APPLICATION_PMT 0x4000 +#define AVC_DEBUG_FCP_PAYLOADS 0x8000 static int avc_debug; module_param_named(debug, avc_debug, int, 0644); -MODULE_PARM_DESC(debug, "Verbose logging (default = 0" - ", FCP subactions = " __stringify(AVC_DEBUG_FCP_SUBACTIONS) - ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) - ", or all = -1)"); +MODULE_PARM_DESC(debug, "Verbose logging (none = 0" + ", FCP subactions" + ": READ DESCRIPTOR = " __stringify(AVC_DEBUG_READ_DESCRIPTOR) + ", DSIT = " __stringify(AVC_DEBUG_DSIT) + ", REGISTER_REMOTE_CONTROL = " __stringify(AVC_DEBUG_REGISTER_REMOTE_CONTROL) + ", LNB CONTROL = " __stringify(AVC_DEBUG_LNB_CONTROL) + ", TUNE QPSK = " __stringify(AVC_DEBUG_TUNE_QPSK) + ", TUNE QPSK2 = " __stringify(AVC_DEBUG_TUNE_QPSK2) + ", HOST2CA = " __stringify(AVC_DEBUG_HOST2CA) + ", CA2HOST = " __stringify(AVC_DEBUG_CA2HOST) + "; Application sent PMT = " __stringify(AVC_DEBUG_APPLICATION_PMT) + ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) + ", or a combination, or all = -1)"); static const char *debug_fcp_ctype(unsigned int ctype) { @@ -118,48 +136,70 @@ static const char *debug_fcp_opcode(unsigned int opcode, const u8 *data, int length) { switch (opcode) { - case AVC_OPCODE_VENDOR: break; - case AVC_OPCODE_READ_DESCRIPTOR: return "ReadDescriptor"; - case AVC_OPCODE_DSIT: return "DirectSelectInfo.Type"; - case AVC_OPCODE_DSD: return "DirectSelectData"; - default: return "?"; + case AVC_OPCODE_VENDOR: + break; + case AVC_OPCODE_READ_DESCRIPTOR: + return avc_debug & AVC_DEBUG_READ_DESCRIPTOR ? + "ReadDescriptor" : NULL; + case AVC_OPCODE_DSIT: + return avc_debug & AVC_DEBUG_DSIT ? + "DirectSelectInfo.Type" : NULL; + case AVC_OPCODE_DSD: + return avc_debug & AVC_DEBUG_DSD ? "DirectSelectData" : NULL; + default: + return "Unknown"; } if (length < 7 || data[3] != SFE_VENDOR_DE_COMPANYID_0 || data[4] != SFE_VENDOR_DE_COMPANYID_1 || data[5] != SFE_VENDOR_DE_COMPANYID_2) - return "Vendor"; + return "Vendor/Unknown"; switch (data[6]) { - case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: return "RegisterRC"; - case SFE_VENDOR_OPCODE_LNB_CONTROL: return "LNBControl"; - case SFE_VENDOR_OPCODE_TUNE_QPSK: return "TuneQPSK"; - case SFE_VENDOR_OPCODE_TUNE_QPSK2: return "TuneQPSK2"; - case SFE_VENDOR_OPCODE_HOST2CA: return "Host2CA"; - case SFE_VENDOR_OPCODE_CA2HOST: return "CA2Host"; + case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: + return avc_debug & AVC_DEBUG_REGISTER_REMOTE_CONTROL ? + "RegisterRC" : NULL; + case SFE_VENDOR_OPCODE_LNB_CONTROL: + return avc_debug & AVC_DEBUG_LNB_CONTROL ? "LNBControl" : NULL; + case SFE_VENDOR_OPCODE_TUNE_QPSK: + return avc_debug & AVC_DEBUG_TUNE_QPSK ? "TuneQPSK" : NULL; + case SFE_VENDOR_OPCODE_TUNE_QPSK2: + return avc_debug & AVC_DEBUG_TUNE_QPSK2 ? "TuneQPSK2" : NULL; + case SFE_VENDOR_OPCODE_HOST2CA: + return avc_debug & AVC_DEBUG_HOST2CA ? "Host2CA" : NULL; + case SFE_VENDOR_OPCODE_CA2HOST: + return avc_debug & AVC_DEBUG_CA2HOST ? "CA2Host" : NULL; } - return "Vendor"; + return "Vendor/Unknown"; } static void debug_fcp(const u8 *data, int length) { - unsigned int subunit_type, subunit_id, op; - const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; + unsigned int subunit_type, subunit_id, opcode; + const char *op, *prefix; + + prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; + subunit_type = data[1] >> 3; + subunit_id = data[1] & 7; + opcode = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; + op = debug_fcp_opcode(opcode, data, length); - if (avc_debug & AVC_DEBUG_FCP_SUBACTIONS) { - subunit_type = data[1] >> 3; - subunit_id = data[1] & 7; - op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; + if (op) { printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", prefix, subunit_type, subunit_id, length, - debug_fcp_ctype(data[0]), - debug_fcp_opcode(op, data, length)); + debug_fcp_ctype(data[0]), op); + if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, + 16, 1, data, length, false); } +} - if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) - print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, 16, 1, - data, length, false); +static void debug_pmt(char *msg, int length) +{ + printk(KERN_INFO "APP PMT -> l=%d\n", length); + print_hex_dump(KERN_INFO, "APP PMT -> ", DUMP_PREFIX_NONE, + 16, 1, msg, length, false); } static int __avc_write(struct firedtv *fdtv, @@ -254,6 +294,26 @@ int avc_recv(struct firedtv *fdtv, void *data, size_t length) return 0; } +static int add_pid_filter(struct firedtv *fdtv, u8 *operand) +{ + int i, n, pos = 1; + + for (i = 0, n = 0; i < 16; i++) { + if (test_bit(i, &fdtv->channel_active)) { + operand[pos++] = 0x13; /* flowfunction relay */ + operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ + operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f; + operand[pos++] = fdtv->channel_pid[i] & 0xff; + operand[pos++] = 0x00; /* tableID */ + operand[pos++] = 0x00; /* filter_length */ + n++; + } + } + operand[0] = n; + + return pos; +} + /* * tuning command for setting the relative LNB frequency * (not supported by the AVC standard) @@ -316,7 +376,8 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv, } } -static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, +static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, struct avc_command_frame *c) { c->opcode = AVC_OPCODE_DSD; @@ -378,13 +439,13 @@ static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, c->operand[20] = 0x00; c->operand[21] = 0x00; - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - c->operand[22] = 0x00; - c->length = 28; + /* Add PIDs to filter */ + c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4); } -static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, +static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, struct avc_command_frame *c) { struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm; @@ -481,10 +542,9 @@ static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, c->operand[15] = 0x00; /* network_ID[0] */ c->operand[16] = 0x00; /* network_ID[1] */ - /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ - c->operand[17] = 0x00; - c->length = 24; + /* Add PIDs to filter */ + c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4); } int avc_tuner_dsd(struct firedtv *fdtv, @@ -502,8 +562,8 @@ int avc_tuner_dsd(struct firedtv *fdtv, switch (fdtv->type) { case FIREDTV_DVB_S: case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break; - case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break; - case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break; + case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params, c); break; + case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params, c); break; default: BUG(); } @@ -963,6 +1023,9 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) int es_info_length; int crc32_csum; + if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT)) + debug_pmt(msg, length); + memset(c, 0, sizeof(*c)); c->ctype = AVC_CTYPE_CONTROL; diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index be967ac09a3..d7c4837fa71 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -81,6 +81,13 @@ config DVB_ZL10036 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_ZL10039 + tristate "Zarlink ZL10039 silicon tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + config DVB_S5H1420 tristate "Samsung S5H1420 based" depends on DVB_CORE && I2C @@ -477,6 +484,14 @@ config DVB_S921 AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. Say Y when you want to support this frontend. +config DVB_DIB8000 + tristate "DiBcom 8000MB/MC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 832473c1e51..3523767e7a7 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o +obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o @@ -31,6 +32,7 @@ obj-$(CONFIG_DVB_SP887X) += sp887x.o obj-$(CONFIG_DVB_NXT6000) += nxt6000.o obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10036) += zl10036.o +obj-$(CONFIG_DVB_ZL10039) += zl10039.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 9e9a75576a1..74981ee923c 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -792,6 +792,11 @@ static int au8522_probe(struct i2c_client *client, } demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); + if (demod_config == NULL) { + if (instance == 1) + kfree(state); + return -ENOMEM; + } demod_config->demod_address = 0x8e >> 1; state->config = demod_config; diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c index ace5cb17165..5fbc0fc37ec 100644 --- a/drivers/media/dvb/frontends/cx22700.c +++ b/drivers/media/dvb/frontends/cx22700.c @@ -155,7 +155,7 @@ static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_paramet p->hierarchy_information > HIERARCHY_4) return -EINVAL; - if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ) + if (p->bandwidth < BANDWIDTH_8_MHZ || p->bandwidth > BANDWIDTH_6_MHZ) return -EINVAL; if (p->bandwidth == BANDWIDTH_7_MHZ) @@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct cx22700_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index 5d1abe34bdd..00b5c7e91d5 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, struct cx22702_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index 87ae29db024..ffbcfabd83f 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, int ret; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c index e4fd533a427..075b2b57cf0 100644 --- a/drivers/media/dvb/frontends/cx24113.c +++ b/drivers/media/dvb/frontends/cx24113.c @@ -303,6 +303,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) { s32 N; s64 F; + u64 dividend; u8 R, r; u8 vcodiv; u8 factor; @@ -346,7 +347,10 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) F = freq_hz; F *= (u64) (R * vcodiv * 262144); dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); - do_div(F, state->config->xtal_khz*1000 * factor * 2); + /* do_div needs an u64 as first argument */ + dividend = F; + do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); + F = dividend; dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); F -= (N + 32) * 262144; diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c index 0592f043ea6..d8f921b6faf 100644 --- a/drivers/media/dvb/frontends/cx24123.c +++ b/drivers/media/dvb/frontends/cx24123.c @@ -458,7 +458,7 @@ static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) /* check if symbol rate is within limits */ if ((srate > state->frontend.ops.info.symbol_rate_max) || (srate < state->frontend.ops.info.symbol_rate_min)) - return -EOPNOTSUPP;; + return -EOPNOTSUPP; /* choose the sampling rate high enough for the required operation, while optimizing the power consumed by the demodulator */ diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c index fe895bf7b18..2be17b93e0b 100644 --- a/drivers/media/dvb/frontends/dib0070.c +++ b/drivers/media/dvb/frontends/dib0070.c @@ -1,12 +1,29 @@ /* * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. * - * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) * * 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, version 2. + * 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. + * + * + * This code is more or less generated from another driver, please + * excuse some codingstyle oddities. + * */ + #include <linux/kernel.h> #include <linux/i2c.h> @@ -19,27 +36,65 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); -#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB0070: "); printk(args); printk("\n"); } } while (0) +#define dprintk(args...) do { \ + if (debug) { \ + printk(KERN_DEBUG "DiB0070: "); \ + printk(args); \ + printk("\n"); \ + } \ +} while (0) #define DIB0070_P1D 0x00 #define DIB0070_P1F 0x01 #define DIB0070_P1G 0x03 #define DIB0070S_P1A 0x02 +enum frontend_tune_state { + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, +}; + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + struct dib0070_state { struct i2c_adapter *i2c; struct dvb_frontend *fe; const struct dib0070_config *cfg; u16 wbd_ff_offset; u8 revision; + + enum frontend_tune_state tune_state; + u32 current_rf; + + /* for the captrim binary search */ + s8 step; + u16 adc_diff; + + s8 captrim; + s8 fcaptrim; + u16 lo4; + + const struct dib0070_tuning *current_tune_table_index; + const struct dib0070_lna_match *lna_match; + + u8 wbd_gain_current; + u16 wbd_offset_3_3[2]; }; static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) { u8 b[2]; struct i2c_msg msg[2] = { - { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, - { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2 }, + {.addr = state->cfg->i2c_address,.flags = 0,.buf = ®,.len = 1}, + {.addr = state->cfg->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2}, }; if (i2c_transfer(state->i2c, msg, 2) != 2) { printk(KERN_WARNING "DiB0070 I2C read failed\n"); @@ -51,7 +106,7 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) { u8 b[3] = { reg, val >> 8, val & 0xff }; - struct i2c_msg msg = { .addr = state->cfg->i2c_address, .flags = 0, .buf = b, .len = 3 }; + struct i2c_msg msg = {.addr = state->cfg->i2c_address,.flags = 0,.buf = b,.len = 3 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0070 I2C write failed\n"); return -EREMOTEIO; @@ -59,55 +114,71 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) return 0; } -#define HARD_RESET(state) do { if (state->cfg->reset) { state->cfg->reset(state->fe,1); msleep(10); state->cfg->reset(state->fe,0); msleep(10); } } while (0) +#define HARD_RESET(state) do { \ + state->cfg->sleep(state->fe, 0); \ + if (state->cfg->reset) { \ + state->cfg->reset(state->fe,1); msleep(10); \ + state->cfg->reset(state->fe,0); msleep(10); \ + } \ +} while (0) static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) { - struct dib0070_state *st = fe->tuner_priv; - u16 tmp = 0; - tmp = dib0070_read_reg(st, 0x02) & 0x3fff; + struct dib0070_state *state = fe->tuner_priv; + u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; + + if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 7000) + tmp |= (0 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 6000) + tmp |= (1 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); - switch(BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)) { - case 8000: - tmp |= (0 << 14); - break; - case 7000: - tmp |= (1 << 14); - break; - case 6000: - tmp |= (2 << 14); - break; - case 5000: - default: - tmp |= (3 << 14); - break; + dib0070_write_reg(state, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(state, 0x17); + + dib0070_write_reg(state, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(state, 0x01) & 0x01ff; + dib0070_write_reg(state, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(state, 0x17, value); } - dib0070_write_reg(st, 0x02, tmp); return 0; } -static void dib0070_captrim(struct dib0070_state *st, u16 LO4) +static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state) { - int8_t captrim, fcaptrim, step_sign, step; - u16 adc, adc_diff = 3000; + int8_t step_sign; + u16 adc; + int ret = 0; + if (*tune_state == CT_TUNER_STEP_0) { + dib0070_write_reg(state, 0x0f, 0xed10); + dib0070_write_reg(state, 0x17, 0x0034); - dib0070_write_reg(st, 0x0f, 0xed10); - dib0070_write_reg(st, 0x17, 0x0034); + dib0070_write_reg(state, 0x18, 0x0032); + state->step = state->captrim = state->fcaptrim = 64; + state->adc_diff = 3000; + ret = 20; - dib0070_write_reg(st, 0x18, 0x0032); - msleep(2); + *tune_state = CT_TUNER_STEP_1; + } else if (*tune_state == CT_TUNER_STEP_1) { + state->step /= 2; + dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); + ret = 15; - step = captrim = fcaptrim = 64; + *tune_state = CT_TUNER_STEP_2; + } else if (*tune_state == CT_TUNER_STEP_2) { - do { - step /= 2; - dib0070_write_reg(st, 0x14, LO4 | captrim); - msleep(1); - adc = dib0070_read_reg(st, 0x19); + adc = dib0070_read_reg(state, 0x19); - dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", captrim, adc, (u32) adc*(u32)1800/(u32)1024); + dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc * (u32) 1800 / (u32) 1024); if (adc >= 400) { adc -= 400; @@ -117,379 +188,430 @@ static void dib0070_captrim(struct dib0070_state *st, u16 LO4) step_sign = 1; } - if (adc < adc_diff) { - dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", captrim, adc, adc_diff); - adc_diff = adc; - fcaptrim = captrim; + if (adc < state->adc_diff) { + dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff); + state->adc_diff = adc; + state->fcaptrim = state->captrim; + } + state->captrim += (step_sign * state->step); + if (state->step >= 1) + *tune_state = CT_TUNER_STEP_1; + else + *tune_state = CT_TUNER_STEP_3; - } - captrim += (step_sign * step); - } while (step >= 1); + } else if (*tune_state == CT_TUNER_STEP_3) { + dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim); + dib0070_write_reg(state, 0x18, 0x07ff); + *tune_state = CT_TUNER_STEP_4; + } - dib0070_write_reg(st, 0x14, LO4 | fcaptrim); - dib0070_write_reg(st, 0x18, 0x07ff); + return ret; } -#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 -#define LO4_SET_VCO_HFDIV(l, v, h) l |= ((v) << 11) | ((h) << 7) -#define LO4_SET_SD(l, s) l |= ((s) << 14) | ((s) << 12) -#define LO4_SET_CTRIM(l, c) l |= (c) << 10 -static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) { - struct dib0070_state *st = fe->tuner_priv; - u32 freq = ch->frequency/1000 + (BAND_OF_FREQUENCY(ch->frequency/1000) == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); - - u8 band = BAND_OF_FREQUENCY(freq), c; + struct dib0070_state *state = fe->tuner_priv; + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + dprintk("CTRL_LO5: 0x%x", lo5); + return dib0070_write_reg(state, 0x15, lo5); +} - /*******************VCO***********************************/ - u16 lo4 = 0; +void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +{ + struct dib0070_state *state = fe->tuner_priv; - u8 REFDIV, PRESC = 2; - u32 FBDiv, Rest, FREF, VCOF_kHz; - u16 Num, Den; - /*******************FrontEnd******************************/ - u16 value = 0; + if (open) { + dib0070_write_reg(state, 0x1b, 0xff00); + dib0070_write_reg(state, 0x1a, 0x0000); + } else { + dib0070_write_reg(state, 0x1b, 0x4112); + if (state->cfg->vga_filter != 0) { + dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); + dprintk("vga filter register is set to %x", state->cfg->vga_filter); + } else + dib0070_write_reg(state, 0x1a, 0x0009); + } +} - dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); +EXPORT_SYMBOL(dib0070_ctrl_agc_filter); +struct dib0070_tuning { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; +}; +struct dib0070_lna_match { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; +}; - dib0070_write_reg(st, 0x17, 0x30); +static const struct dib0070_tuning dib0070s_tuning_table[] = { + {570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800}, /* UHF */ + {700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800}, + {863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800}, + {1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND */ + {1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, + {2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, + {0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000}, /* SBAND */ +}; - dib0070_set_bandwidth(fe, ch); /* c is used as HF */ - switch (st->revision) { - case DIB0070S_P1A: - switch (band) { - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 2; - break; - case BAND_SBAND: - LO4_SET_VCO_HFDIV(lo4, 0, 0); - LO4_SET_CTRIM(lo4, 1);; - c = 1; - break; - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - PRESC = 6; c = 6; - } else if (freq < 680000) { - LO4_SET_VCO_HFDIV(lo4, 0, 2); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 2); - c = 4; - } - break; - } break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - switch (band) { - case BAND_FM: - LO4_SET_VCO_HFDIV(lo4, 0, 7); - c = 24; - break; - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 0); - c = 2; - break; - case BAND_VHF: - if (freq < 180000) { - LO4_SET_VCO_HFDIV(lo4, 0, 3); - c = 16; - } else if (freq < 190000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - c = 16; - } else { - LO4_SET_VCO_HFDIV(lo4, 0, 6); - c = 12; - } - break; - - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 5); - c = 6; - } else if (freq < 700000) { - LO4_SET_VCO_HFDIV(lo4, 0, 1); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 4; - } - break; - } - break; - } +static const struct dib0070_tuning dib0070_tuning_table[] = { + {115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000}, /* FM below 92MHz cannot be tuned */ + {179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000}, /* VHF */ + {189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000}, + {250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000}, + {569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800}, /* UHF */ + {699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800}, + {863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800}, + {0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND or everything higher than UHF */ +}; - dprintk( "HFDIV code: %hd", (lo4 >> 7) & 0xf); - dprintk( "VCO = %hd", (lo4 >> 11) & 0x3); +static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { + {180000, 0}, /* VHF */ + {188000, 1}, + {196400, 2}, + {250000, 3}, + {550000, 0}, /* UHF */ + {590000, 1}, + {666000, 3}, + {864000, 5}, + {1500000, 0}, /* LBAND or everything higher than UHF */ + {1600000, 1}, + {2000000, 3}, + {0xffffffff, 7}, +}; +static const struct dib0070_lna_match dib0070_lna[] = { + {180000, 0}, /* VHF */ + {188000, 1}, + {196400, 2}, + {250000, 3}, + {550000, 2}, /* UHF */ + {650000, 3}, + {750000, 5}, + {850000, 6}, + {864000, 7}, + {1500000, 0}, /* LBAND or everything higher than UHF */ + {1600000, 1}, + {2000000, 3}, + {0xffffffff, 7}, +}; - VCOF_kHz = (c * freq) * 2; - dprintk( "VCOF in kHz: %d ((%hd*%d) << 1))",VCOF_kHz, c, freq); +#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 +static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +{ + struct dib0070_state *state = fe->tuner_priv; - switch (band) { - case BAND_VHF: - REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); - break; - case BAND_FM: - REFDIV = (u8) ((st->cfg->clock_khz) / 1000); - break; - default: - REFDIV = (u8) ( st->cfg->clock_khz / 10000); - break; - } - FREF = st->cfg->clock_khz / REFDIV; + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; - dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ + u8 band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000); + u32 freq = fe->dtv_property_cache.frequency / 1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); +#ifdef CONFIG_SYS_ISDBT + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) + if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) + freq += 850; +#endif + if (state->current_rf != freq) { - switch (st->revision) { + switch (state->revision) { case DIB0070S_P1A: - FBDiv = (VCOF_kHz / PRESC / FREF); - Rest = (VCOF_kHz / PRESC) - FBDiv * FREF; + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; break; - - case DIB0070_P1G: - case DIB0070_P1F: default: - FBDiv = (freq / (FREF / 2)); - Rest = 2 * freq - FBDiv * FREF; + tune = dib0070_tuning_table; + if (state->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; break; - } - - - if (Rest < LPF) Rest = 0; - else if (Rest < 2 * LPF) Rest = 2 * LPF; - else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } - else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; - Rest = (Rest * 6528) / (FREF / 10); - dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); - - Num = 0; - Den = 1; + } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; - if (Rest > 0) { - LO4_SET_SD(lo4, 1); - Den = 255; - Num = (u16)Rest; + state->current_tune_table_index = tune; + state->lna_match = lna_match; } - dprintk( "Num: %hd, Den: %hd, SD: %hd",Num, Den, (lo4 >> 12) & 0x1); + if (*tune_state == CT_TUNER_START) { + dprintk("Tuning for Band: %hd (%d kHz)", band, freq); + if (state->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; + state->current_rf = freq; + state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); - dib0070_write_reg(st, 0x11, (u16)FBDiv); + dib0070_write_reg(state, 0x17, 0x30); + VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; - dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); - + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((state->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) (state->cfg->clock_khz / 10000); + break; + } + FREF = state->cfg->clock_khz / REFDIV; + + switch (state->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; + break; + + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } - dib0070_write_reg(st, 0x13, Num); + if (Rest < LPF) + Rest = 0; + else if (Rest < 2 * LPF) + Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { + Rest = 0; + FBDiv += 1; + } else if (Rest > (FREF - 2 * LPF)) + Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); + + Den = 1; + if (Rest > 0) { + state->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + dib0070_write_reg(state, 0x11, (u16) FBDiv); + dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(state, 0x13, (u16) Rest); - value = 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001; + if (state->revision == DIB0070S_P1A) { - switch (band) { - case BAND_UHF: value |= 0x4000 | 0x0800; break; - case BAND_LBAND: value |= 0x2000 | 0x0400; break; - default: value |= 0x8000 | 0x1000; break; - } - dib0070_write_reg(st, 0x20, value); + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(state, 0x1d, 0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } - dib0070_captrim(st, lo4); - if (st->revision == DIB0070S_P1A) { - if (band == BAND_SBAND) - dib0070_write_reg(st, 0x15, 0x16e2); - else - dib0070_write_reg(st, 0x15, 0x56e5); - } + dib0070_write_reg(state, 0x20, + 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); + dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); + dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); + dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); + dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); + dprintk("VCO = %hd", state->current_tune_table_index->vco_band); + dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; + } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + + ret = dib0070_captrim(state, tune_state); + + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + if (tmp != NULL) { + while (freq / 1000 > tmp->freq) /* find the right one */ + tmp++; + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (state-> + current_tune_table_index-> + wbdmux << 0)); + state->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index-> + wbdmux << 0)); + state->wbd_gain_current = 6; + } - switch (band) { - case BAND_UHF: value = 0x7c82; break; - case BAND_LBAND: value = 0x7c84; break; - default: value = 0x7c81; break; - } - dib0070_write_reg(st, 0x0f, value); - dib0070_write_reg(st, 0x06, 0x3fff); - - /* Front End */ - /* c == TUNE, value = SWITCH */ - c = 0; - value = 0; - switch (band) { - case BAND_FM: - c = 0; value = 1; - break; - - case BAND_VHF: - if (freq <= 180000) c = 0; - else if (freq <= 188200) c = 1; - else if (freq <= 196400) c = 2; - else c = 3; - value = 1; - break; - - case BAND_LBAND: - if (freq <= 1500000) c = 0; - else if (freq <= 1600000) c = 1; - else c = 3; - break; - - case BAND_SBAND: - c = 7; - dib0070_write_reg(st, 0x1d,0xFFFF); - break; - - case BAND_UHF: - default: - if (st->cfg->flip_chip) { - if (freq <= 550000) c = 0; - else if (freq <= 590000) c = 1; - else if (freq <= 666000) c = 3; - else c = 5; - } else { - if (freq <= 550000) c = 2; - else if (freq <= 650000) c = 3; - else if (freq <= 750000) c = 5; - else if (freq <= 850000) c = 6; - else c = 7; - } - value = 2; - break; + dib0070_write_reg(state, 0x06, 0x3fff); + dib0070_write_reg(state, 0x07, + (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); + dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); + dib0070_write_reg(state, 0x0d, 0x0d80); + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x17, 0x0033); + + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe, ch); + *tune_state = CT_TUNER_STOP; + } else { + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ } + return ret; +} - /* default: LNA_MATCH=7, BIAS=3 */ - dib0070_write_reg(st, 0x07, (value << 11) | (7 << 8) | (c << 3) | (3 << 0)); - dib0070_write_reg(st, 0x08, (c << 10) | (3 << 7) | (127)); - dib0070_write_reg(st, 0x0d, 0x0d80); +static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; + state->tune_state = CT_TUNER_START; - dib0070_write_reg(st, 0x18, 0x07ff); - dib0070_write_reg(st, 0x17, 0x0033); + do { + ret = dib0070_tune_digital(fe, p); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret / 10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); return 0; } static int dib0070_wakeup(struct dvb_frontend *fe) { - struct dib0070_state *st = fe->tuner_priv; - if (st->cfg->sleep) - st->cfg->sleep(fe, 0); + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 0); return 0; } static int dib0070_sleep(struct dvb_frontend *fe) { - struct dib0070_state *st = fe->tuner_priv; - if (st->cfg->sleep) - st->cfg->sleep(fe, 1); + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 1); return 0; } -static u16 dib0070_p1f_defaults[] = - -{ +static const u16 dib0070_p1f_defaults[] = { 7, 0x02, - 0x0008, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0002, - 0x0100, + 0x0008, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0002, + 0x0100, 3, 0x0d, - 0x0d80, - 0x0001, - 0x0000, + 0x0d80, + 0x0001, + 0x0000, 4, 0x11, - 0x0000, - 0x0103, - 0x0000, - 0x0000, + 0x0000, + 0x0103, + 0x0000, + 0x0000, 3, 0x16, - 0x0004 | 0x0040, - 0x0030, - 0x07ff, + 0x0004 | 0x0040, + 0x0030, + 0x07ff, 6, 0x1b, - 0x4112, - 0xff00, - 0xc07f, - 0x0000, - 0x0180, - 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, + 0x4112, + 0xff00, + 0xc07f, + 0x0000, + 0x0180, + 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, 0, }; -static void dib0070_wbd_calibration(struct dvb_frontend *fe) +static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) { - u16 wbd_offs; - struct dib0070_state *state = fe->tuner_priv; - - if (state->cfg->sleep) - state->cfg->sleep(fe, 0); + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; - dib0070_write_reg(state, 0x0f, 0x6d81); - dib0070_write_reg(state, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); msleep(9); - wbd_offs = dib0070_read_reg(state, 0x19); - dib0070_write_reg(state, 0x20, 0); - state->wbd_ff_offset = ((wbd_offs * 8 * 18 / 33 + 1) / 2); - dprintk( "WBDStart = %d (Vargen) - FF = %hd", (u32) wbd_offs * 1800/1024, state->wbd_ff_offset); - - if (state->cfg->sleep) - state->cfg->sleep(fe, 1); - + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; } -u16 dib0070_wbd_offset(struct dvb_frontend *fe) +static void dib0070_wbd_offset_calibration(struct dib0070_state *state) { - struct dib0070_state *st = fe->tuner_priv; - return st->wbd_ff_offset; + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain - 6]); + } } -EXPORT_SYMBOL(dib0070_wbd_offset); -static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) +u16 dib0070_wbd_offset(struct dvb_frontend *fe) { struct dib0070_state *state = fe->tuner_priv; - u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); - dprintk( "CTRL_LO5: 0x%x", lo5); - return dib0070_write_reg(state, 0x15, lo5); + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + u32 freq = fe->dtv_property_cache.frequency / 1000; + + if (tmp != NULL) { + while (freq / 1000 > tmp->freq) /* find the right one */ + tmp++; + state->wbd_gain_current = tmp->wbd_gain_val; + } else + state->wbd_gain_current = 6; + + return state->wbd_offset_3_3[state->wbd_gain_current - 6]; } +EXPORT_SYMBOL(dib0070_wbd_offset); + #define pgm_read_word(w) (*w) -static int dib0070_reset(struct dib0070_state *state) +static int dib0070_reset(struct dvb_frontend *fe) { + struct dib0070_state *state = fe->tuner_priv; u16 l, r, *n; HARD_RESET(state); - #ifndef FORCE_SBAND_TUNER if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; else +#else +#warning forcing SBAND #endif - state->revision = DIB0070S_P1A; + state->revision = DIB0070S_P1A; /* P1F or not */ - dprintk( "Revision: %x", state->revision); + dprintk("Revision: %x", state->revision); if (state->revision == DIB0070_P1D) { - dprintk( "Error: this driver is not to be used meant for P1D or earlier"); + dprintk("Error: this driver is not to be used meant for P1D or earlier"); return -EINVAL; } @@ -498,7 +620,7 @@ static int dib0070_reset(struct dib0070_state *state) while (l) { r = pgm_read_word(n++); do { - dib0070_write_reg(state, (u8)r, pgm_read_word(n++)); + dib0070_write_reg(state, (u8) r, pgm_read_word(n++)); r++; } while (--l); l = pgm_read_word(n++); @@ -514,24 +636,25 @@ static int dib0070_reset(struct dib0070_state *state) r |= state->cfg->osc_buffer_state << 3; dib0070_write_reg(state, 0x10, r); - dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 4)); + dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); if (state->cfg->invert_iq) { r = dib0070_read_reg(state, 0x02) & 0xffdf; dib0070_write_reg(state, 0x02, r | (1 << 5)); } - if (state->revision == DIB0070S_P1A) - dib0070_set_ctrl_lo5(state->fe, 4, 7, 3, 1); + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); else - dib0070_set_ctrl_lo5(state->fe, 4, 4, 2, 0); + dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); + + dib0070_wbd_offset_calibration(state); + return 0; } - static int dib0070_release(struct dvb_frontend *fe) { kfree(fe->tuner_priv); @@ -539,23 +662,24 @@ static int dib0070_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops dib0070_ops = { +static const struct dvb_tuner_ops dib0070_ops = { .info = { - .name = "DiBcom DiB0070", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, - }, - .release = dib0070_release, - - .init = dib0070_wakeup, - .sleep = dib0070_sleep, - .set_params = dib0070_tune_digital, -// .get_frequency = dib0070_get_frequency, -// .get_bandwidth = dib0070_get_bandwidth + .name = "DiBcom DiB0070", + .frequency_min = 45000000, + .frequency_max = 860000000, + .frequency_step = 1000, + }, + .release = dib0070_release, + + .init = dib0070_wakeup, + .sleep = dib0070_sleep, + .set_params = dib0070_tune, + +// .get_frequency = dib0070_get_frequency, +// .get_bandwidth = dib0070_get_bandwidth }; -struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) { struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL); if (state == NULL) @@ -563,25 +687,24 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter state->cfg = cfg; state->i2c = i2c; - state->fe = fe; + state->fe = fe; fe->tuner_priv = state; - if (dib0070_reset(state) != 0) + if (dib0070_reset(fe) != 0) goto free_mem; - dib0070_wbd_calibration(fe); - printk(KERN_INFO "DiB0070: successfully identified\n"); memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = state; return fe; -free_mem: + free_mem: kfree(state); fe->tuner_priv = NULL; return NULL; } + EXPORT_SYMBOL(dib0070_attach); MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 9670f5d20cf..8a2e1e710ad 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -15,6 +15,11 @@ struct i2c_adapter; #define DEFAULT_DIB0070_I2C_ADDRESS 0x60 +struct dib0070_wbd_gain_cfg { + u16 freq; + u16 wbd_gain_val; +}; + struct dib0070_config { u8 i2c_address; @@ -26,26 +31,28 @@ struct dib0070_config { int freq_offset_khz_uhf; int freq_offset_khz_vhf; - u8 osc_buffer_state; /* 0= normal, 1= tri-state */ - u32 clock_khz; - u8 clock_pad_drive; /* (Drive + 1) * 2mA */ + u8 osc_buffer_state; /* 0= normal, 1= tri-state */ + u32 clock_khz; + u8 clock_pad_drive; /* (Drive + 1) * 2mA */ - u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ + u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ - u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ + u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ u8 flip_chip; + u8 enable_third_order_filter; + u8 charge_pump; + + const struct dib0070_wbd_gain_cfg *wbd_gain; + + u8 vga_filter; }; #if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) -extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dib0070_config *cfg); +extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); extern u16 dib0070_wbd_offset(struct dvb_frontend *); #else -static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dib0070_config *cfg) +static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; @@ -57,5 +64,6 @@ static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) return -ENODEV; } #endif +extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); #endif diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 8217e5b38f4..55ef6eeb076 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> +#include "dvb_math.h" #include "dvb_frontend.h" #include "dib7000p.h" @@ -883,7 +884,7 @@ static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 255, 255, 255, 255, 255, 255}; u32 xtal = state->cfg.bw->xtal_hz / 1000; - int f_rel = ( (rf_khz + xtal/2) / xtal) * xtal - rf_khz; + int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz; int k; int coef_re[8],coef_im[8]; int bw_khz = bw; @@ -1217,7 +1218,37 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) { - *snr = 0x0000; + struct dib7000p_state *state = fe->demodulator_priv; + u16 val; + s32 signal_mant, signal_exp, noise_mant, noise_exp; + u32 result = 0; + + val = dib7000p_read_word(state, 479); + noise_mant = (val >> 4) & 0xff; + noise_exp = ((val & 0xf) << 2); + val = dib7000p_read_word(state, 480); + noise_exp += ((val >> 14) & 0x3); + if ((noise_exp & 0x20) != 0) + noise_exp -= 0x40; + + signal_mant = (val >> 6) & 0xFF; + signal_exp = (val & 0x3F); + if ((signal_exp & 0x20) != 0) + signal_exp -= 0x40; + + if (signal_mant != 0) + result = intlog10(2) * 10 * signal_exp + 10 * + intlog10(signal_mant); + else + result = intlog10(2) * 10 * signal_exp - 100; + + if (noise_mant != 0) + result -= intlog10(2) * 10 * noise_exp + 10 * + intlog10(noise_mant); + else + result -= intlog10(2) * 10 * noise_exp - 100; + + *snr = result / ((1 << 24) / 10); return 0; } diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c new file mode 100644 index 00000000000..852c790d09d --- /dev/null +++ b/drivers/media/dvb/frontends/dib8000.c @@ -0,0 +1,2277 @@ +/* + * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T). + * + * Copyright (C) 2009 DiBcom (http://www.dibcom.fr/) + * + * 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, version 2. + */ +#include <linux/kernel.h> +#include <linux/i2c.h> +#include "dvb_math.h" + +#include "dvb_frontend.h" + +#include "dib8000.h" + +#define LAYER_ALL -1 +#define LAYER_A 1 +#define LAYER_B 2 +#define LAYER_C 3 + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) + +enum frontend_tune_state { + CT_AGC_START = 20, + CT_AGC_STEP_0, + CT_AGC_STEP_1, + CT_AGC_STEP_2, + CT_AGC_STEP_3, + CT_AGC_STEP_4, + CT_AGC_STOP, + + CT_DEMOD_START = 30, +}; + +#define FE_STATUS_TUNE_FAILED 0 + +struct i2c_device { + struct i2c_adapter *adap; + u8 addr; +}; + +struct dib8000_state { + struct dvb_frontend fe; + struct dib8000_config cfg; + + struct i2c_device i2c; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + u32 current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + u32 timf_default; + + u8 div_force_off:1; + u8 div_state:1; + u16 div_sync_wait; + + u8 agc_state; + u8 differential_constellation; + u8 diversity_onoff; + + s16 ber_monitored_layer; + u16 gpio_dir; + u16 gpio_val; + + u16 revision; + u8 isdbt_cfg_loaded; + enum frontend_tune_state tune_state; + u32 status; +}; + +enum dib8000_power_mode { + DIB8000M_POWER_ALL = 0, + DIB8000M_POWER_INTERFACE_ONLY, +}; + +static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) +{ + u8 wb[2] = { reg >> 8, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + {.addr = i2c->addr >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = i2c->addr >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, + }; + + if (i2c_transfer(i2c->adap, msg, 2) != 2) + dprintk("i2c read error on %d", reg); + + return (rb[0] << 8) | rb[1]; +} + +static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +{ + return dib8000_i2c_read16(&state->i2c, reg); +} + +static u32 dib8000_read32(struct dib8000_state *state, u16 reg) +{ + u16 rw[2]; + + rw[0] = dib8000_read_word(state, reg + 0); + rw[1] = dib8000_read_word(state, reg + 1); + + return ((rw[0] << 16) | (rw[1])); +} + +static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = i2c->addr >> 1,.flags = 0,.buf = b,.len = 4 + }; + return i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) +{ + return dib8000_i2c_write16(&state->i2c, reg, val); +} + +const int16_t coeff_2k_sb_1seg_dqpsk[8] = { + (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, + (920 << 5) | 0x09 +}; + +const int16_t coeff_2k_sb_1seg[8] = { + (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f +}; + +const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { + (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, + (-931 << 5) | 0x0f +}; + +const int16_t coeff_2k_sb_3seg_0dqpsk[8] = { + (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, + (982 << 5) | 0x0c +}; + +const int16_t coeff_2k_sb_3seg_1dqpsk[8] = { + (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, + (-720 << 5) | 0x0d +}; + +const int16_t coeff_2k_sb_3seg[8] = { + (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, + (-610 << 5) | 0x0a +}; + +const int16_t coeff_4k_sb_1seg_dqpsk[8] = { + (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, + (-922 << 5) | 0x0d +}; + +const int16_t coeff_4k_sb_1seg[8] = { + (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, + (-655 << 5) | 0x0a +}; + +const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { + (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, + (-958 << 5) | 0x13 +}; + +const int16_t coeff_4k_sb_3seg_0dqpsk[8] = { + (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, + (-568 << 5) | 0x0f +}; + +const int16_t coeff_4k_sb_3seg_1dqpsk[8] = { + (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, + (-848 << 5) | 0x13 +}; + +const int16_t coeff_4k_sb_3seg[8] = { + (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, + (-869 << 5) | 0x13 +}; + +const int16_t coeff_8k_sb_1seg_dqpsk[8] = { + (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, + (-598 << 5) | 0x10 +}; + +const int16_t coeff_8k_sb_1seg[8] = { + (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, + (585 << 5) | 0x0f +}; + +const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { + (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, + (0 << 5) | 0x14 +}; + +const int16_t coeff_8k_sb_3seg_0dqpsk[8] = { + (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, + (-877 << 5) | 0x15 +}; + +const int16_t coeff_8k_sb_3seg_1dqpsk[8] = { + (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, + (-921 << 5) | 0x14 +}; + +const int16_t coeff_8k_sb_3seg[8] = { + (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, + (690 << 5) | 0x14 +}; + +const int16_t ana_fe_coeff_3seg[24] = { + 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 +}; + +const int16_t ana_fe_coeff_1seg[24] = { + 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 +}; + +const int16_t ana_fe_coeff_13seg[24] = { + 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 +}; + +static u16 fft_to_mode(struct dib8000_state *state) +{ + u16 mode; + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + mode = 1; + break; + case TRANSMISSION_MODE_4K: + mode = 2; + break; + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + mode = 3; + break; + } + return mode; +} + +static void dib8000_set_acquisition_mode(struct dib8000_state *state) +{ + u16 nud = dib8000_read_word(state, 298); + nud |= (1 << 3) | (1 << 0); + dprintk("acquisition mode activated"); + dib8000_write_word(state, 298, nud); +} + +static int dib8000_set_output_mode(struct dib8000_state *state, int mode) +{ + u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d", &state->fe, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) { + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + sram &= 0xfdff; + } else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + + case OUTMODE_ANALOG_ADC: + outreg = (1 << 10) | (3 << 6); + dib8000_set_acquisition_mode(state); + break; + + default: + dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe); + return -EINVAL; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + dib8000_write_word(state, 299, smo_mode); + dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */ + dib8000_write_word(state, 1286, outreg); + dib8000_write_word(state, 1291, sram); + + return 0; +} + +static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; + + if (!state->differential_constellation) { + dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 + dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 + } else { + dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0 + dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0 + } + state->diversity_onoff = onoff; + + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dib8000_write_word(state, 270, 1); + dib8000_write_word(state, 271, 0); + break; + case 1: /* both ways */ + dib8000_write_word(state, 270, 6); + dib8000_write_word(state, 271, 6); + break; + case 2: /* only the diversity input */ + dib8000_write_word(state, 270, 0); + dib8000_write_word(state, 271, 1); + break; + } + return 0; +} + +static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, + reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB8000M_POWER_ALL: + reg_774 = 0x0000; + reg_775 = 0x0000; + reg_776 = 0x0000; + reg_900 &= 0xfffc; + reg_1280 &= 0x00ff; + break; + case DIB8000M_POWER_INTERFACE_ONLY: + reg_1280 &= 0x00ff; + break; + } + + dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x", reg_774, reg_775, reg_776, reg_900, reg_1280); + dib8000_write_word(state, 774, reg_774); + dib8000_write_word(state, 775, reg_775); + dib8000_write_word(state, 776, reg_776); + dib8000_write_word(state, 900, reg_900); + dib8000_write_word(state, 1280, reg_1280); +} + +static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg_907 = dib8000_read_word(state, 907), reg_908 = dib8000_read_word(state, 908); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_908 |= (1 << 1) | (1 << 0); + ret |= dib8000_write_word(state, 908, reg_908); + reg_908 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_908 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_907 &= 0x0fff; + reg_908 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_907 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_908 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_907 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_907 |= (1 << 15); + break; + + default: + break; + } + + ret |= dib8000_write_word(state, 907, reg_907); + ret |= dib8000_write_word(state, 908, reg_908); + + return ret; +} + +static int dib8000_set_bandwidth(struct dib8000_state *state, u32 bw) +{ + u32 timf; + + if (bw == 0) + bw = 6000; + + if (state->timf == 0) { + dprintk("using default timf"); + timf = state->timf_default; + } else { + dprintk("using updated timf"); + timf = state->timf; + } + + dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff)); + dib8000_write_word(state, 30, (u16) ((timf) & 0xffff)); + + return 0; +} + +static int dib8000_sad_calib(struct dib8000_state *state) +{ +/* internal */ + dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); + dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib8000_write_word(state, 923, (1 << 0)); + dib8000_write_word(state, 923, (0 << 0)); + + msleep(1); + return 0; +} + +int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + struct dib8000_state *state = fe->demodulator_priv; + if (value > 4095) + value = 4095; + state->wbd_ref = value; + return dib8000_write_word(state, 106, value); +} + +EXPORT_SYMBOL(dib8000_set_wbd_ref); +static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) +{ + dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); + dib8000_write_word(state, 23, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); /* P_sec_len */ + dib8000_write_word(state, 24, (u16) ((bw->internal * 1000) & 0xffff)); + dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003)); + + dib8000_write_word(state, 922, bw->sad_cfg); +} + +static void dib8000_reset_pll(struct dib8000_state *state) +{ + const struct dibx000_bandwidth_config *pll = state->cfg.pll; + u16 clk_cfg1; + + // clk_cfg0 + dib8000_write_word(state, 901, (pll->pll_prediv << 8) | (pll->pll_ratio << 0)); + + // clk_cfg1 + clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | + (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0); + + dib8000_write_word(state, 902, clk_cfg1); + clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); + dib8000_write_word(state, 902, clk_cfg1); + + dprintk("clk_cfg1: 0x%04x", clk_cfg1); /* 0x507 1 0 1 000 0 0 11 1 */ + + /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ + if (state->cfg.pll->ADClkSrc == 0) + dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); + else if (state->cfg.refclksel != 0) + dib8000_write_word(state, 904, + (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll-> + ADClkSrc << 7) | (0 << 1)); + else + dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | (3 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); + + dib8000_reset_pll_common(state, pll); +} + +static int dib8000_reset_gpio(struct dib8000_state *st) +{ + /* reset the GPIOs */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib8000_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) +{ + st->cfg.gpio_dir = dib8000_read_word(st, 1029); + st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + + st->cfg.gpio_val = dib8000_read_word(st, 1030); + st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */ + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + dprintk("gpio dir: %x: gpio val: %x", st->cfg.gpio_dir, st->cfg.gpio_val); + + return 0; +} + +int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + struct dib8000_state *state = fe->demodulator_priv; + return dib8000_cfg_gpio(state, num, dir, val); +} + +EXPORT_SYMBOL(dib8000_set_gpio); +static const u16 dib8000_defaults[] = { + /* auto search configuration - lock0 by default waiting + * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ + 3, 7, + 0x0004, + 0x0400, + 0x0814, + + 12, 11, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, + + /*1, 32, + 0x6680 // P_corm_thres Lock algorithms configuration */ + + 11, 80, /* set ADC level to -16 */ + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, + + 4, 108, + 0, + 0, + 0, + 0, + + 1, 175, + 0x0410, + 1, 179, + 8192, // P_fft_nb_to_cut + + 6, 181, + 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + + 2, 193, + 0x0666, // P_pha3_thres + 0x0000, // P_cti_use_cpe, P_cti_use_prog + + 2, 205, + 0x200f, // P_cspu_regul, P_cspu_win_cut + 0x000f, // P_des_shift_work + + 5, 215, + 0x023d, // P_adp_regul_cnt + 0x00a4, // P_adp_noise_cnt + 0x00a4, // P_adp_regul_ext + 0x7ff0, // P_adp_noise_ext + 0x3ccc, // P_adp_fil + + 1, 230, + 0x0000, // P_2d_byp_ti_num + + 1, 263, + 0x800, //P_equal_thres_wgn + + 1, 268, + (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode + + 1, 270, + 0x0001, // P_div_lock0_wait + 1, 285, + 0x0020, //p_fec_ + 1, 299, + 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + + 1, 338, + (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 + (1 << 10) | // P_ctrl_pre_freq_mode_sat=1 + (0 << 9) | // P_ctrl_pre_freq_inh=0 + (3 << 5) | // P_ctrl_pre_freq_step=3 + (1 << 0), // P_pre_freq_win_len=1 + + 1, 903, + (0 << 4) | 2, // P_divclksel=0 P_divbitsel=2 (was clk=3,bit=1 for MPW) + + 0, +}; + +static u16 dib8000_identify(struct i2c_device *client) +{ + u16 value; + + //because of glitches sometimes + value = dib8000_i2c_read16(client, 896); + + if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) { + dprintk("wrong Vendor ID (read=0x%x)", value); + return 0; + } + + value = dib8000_i2c_read16(client, 897); + if (value != 0x8000 && value != 0x8001 && value != 0x8002) { + dprintk("wrong Device ID (%x)", value); + return 0; + } + + switch (value) { + case 0x8000: + dprintk("found DiB8000A"); + break; + case 0x8001: + dprintk("found DiB8000B"); + break; + case 0x8002: + dprintk("found DiB8000C"); + break; + } + return value; +} + +static int dib8000_reset(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + dib8000_write_word(state, 1287, 0x0003); /* sram lead in, rdy */ + + if ((state->revision = dib8000_identify(&state->i2c)) == 0) + return -EINVAL; + + if (state->revision == 0x8000) + dprintk("error : dib8000 MA not supported"); + + dibx000_reset_i2c_master(&state->i2c_master); + + dib8000_set_power_mode(state, DIB8000M_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib8000_write_word(state, 770, 0xffff); + dib8000_write_word(state, 771, 0xffff); + dib8000_write_word(state, 772, 0xfffc); + dib8000_write_word(state, 898, 0x000c); // sad + dib8000_write_word(state, 1280, 0x004d); + dib8000_write_word(state, 1281, 0x000c); + + dib8000_write_word(state, 770, 0x0000); + dib8000_write_word(state, 771, 0x0000); + dib8000_write_word(state, 772, 0x0000); + dib8000_write_word(state, 898, 0x0004); // sad + dib8000_write_word(state, 1280, 0x0000); + dib8000_write_word(state, 1281, 0x0000); + + /* drives */ + if (state->cfg.drives) + dib8000_write_word(state, 906, state->cfg.drives); + else { + dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal."); + dib8000_write_word(state, 906, 0x2d98); // min drive SDRAM - not optimal - adjust + } + + dib8000_reset_pll(state); + + if (dib8000_reset_gpio(state) != 0) + dprintk("GPIO reset was not successful."); + + if (dib8000_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("OUTPUT_MODE could not be resetted."); + + state->current_agc = NULL; + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */ + if (state->cfg.pll->ifreq == 0) + dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */ + else + dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */ + + { + u16 l = 0, r; + const u16 *n; + n = dib8000_defaults; + l = *n++; + while (l) { + r = *n++; + do { + dib8000_write_word(state, r, *n++); + r++; + } while (--l); + l = *n++; + } + } + state->isdbt_cfg_loaded = 0; + + //div_cfg override for special configs + if (state->cfg.div_cfg != 0) + dib8000_write_word(state, 903, state->cfg.div_cfg); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); + + dib8000_set_bandwidth(state, 6000); + + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib8000_sad_calib(state); + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + dib8000_set_power_mode(state, DIB8000M_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib8000_restart_agc(struct dib8000_state *state) +{ + // P_restart_iqc & P_restart_agc + dib8000_write_word(state, 770, 0x0a00); + dib8000_write_word(state, 770, 0x0000); +} + +static int dib8000_update_lna(struct dib8000_state *state) +{ + u16 dyn_gain; + + if (state->cfg.update_lna) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib8000_read_word(state, 390); + + if (state->cfg.update_lna(&state->fe, dyn_gain)) { // LNA has changed + dib8000_restart_agc(state); + return 1; + } + } + return 0; +} + +static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band && state->current_agc != NULL) + return 0; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("no valid AGC configuration found for band 0x%02x", band); + return -EINVAL; + } + + state->current_agc = agc; + + /* AGC */ + dib8000_write_word(state, 76, agc->setup); + dib8000_write_word(state, 77, agc->inv_gain); + dib8000_write_word(state, 78, agc->time_stabiliz); + dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp); + dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib8000_write_word(state, 106, state->wbd_ref); + else // use default + dib8000_write_word(state, 106, agc->wbd_ref); + dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); + dib8000_write_word(state, 108, agc->agc1_max); + dib8000_write_word(state, 109, agc->agc1_min); + dib8000_write_word(state, 110, agc->agc2_max); + dib8000_write_word(state, 111, agc->agc2_min); + dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + dib8000_write_word(state, 75, agc->agc1_pt3); + dib8000_write_word(state, 923, (dib8000_read_word(state, 923) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); /*LB : 929 -> 923 */ + + return 0; +} + +static int dib8000_agc_soft_split(struct dib8000_state *state) +{ + u16 agc, split_offset; + + if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return FE_CALLBACK_TIME_NEVER; + + // n_agc_global + agc = dib8000_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d", split_offset); + + // P_agc_force_split and P_agc_split_offset + dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset); + return 5000; +} + +static int dib8000_agc_startup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + enum frontend_tune_state *tune_state = &state->tune_state; + + int ret = 0; + + switch (*tune_state) { + case CT_AGC_START: + // set power-up level: interf+analog+AGC + + dib8000_set_adc_state(state, DIBX000_ADC_ON); + + if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) { + *tune_state = CT_AGC_STOP; + state->status = FE_STATUS_TUNE_FAILED; + break; + } + + ret = 70; + *tune_state = CT_AGC_STEP_0; + break; + + case CT_AGC_STEP_0: + //AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->fe, 1); + + dib8000_restart_agc(state); + + // wait AGC rough lock time + ret = 50; + *tune_state = CT_AGC_STEP_1; + break; + + case CT_AGC_STEP_1: + // wait AGC accurate lock time + ret = 70; + + if (dib8000_update_lna(state)) + // wait only AGC rough lock time + ret = 50; + else + *tune_state = CT_AGC_STEP_2; + break; + + case CT_AGC_STEP_2: + dib8000_agc_soft_split(state); + + if (state->cfg.agc_control) + state->cfg.agc_control(&state->fe, 0); + + *tune_state = CT_AGC_STOP; + break; + default: + ret = dib8000_agc_soft_split(state); + break; + } + return ret; + +} + +static void dib8000_update_timf(struct dib8000_state *state) +{ + u32 timf = state->timf = dib8000_read32(state, 435); + + dib8000_write_word(state, 29, (u16) (timf >> 16)); + dib8000_write_word(state, 30, (u16) (timf & 0xffff)); + dprintk("Updated timing frequency: %d (default: %d)", state->timf, state->timf_default); +} + +static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +{ + u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; + u8 guard, crate, constellation, timeI; + u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; + u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled + const s16 *ncoeff, *ana_fe; + u16 tmcc_pow = 0; + u16 coff_pow = 0x2800; + u16 init_prbs = 0xfff; + u16 ana_gain = 0; + u16 adc_target_16dB[11] = { + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117 + }; + + if (state->ber_monitored_layer != LAYER_ALL) + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); + else + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + + i = dib8000_read_word(state, 26) & 1; // P_dds_invspec + dib8000_write_word(state, 26, state->fe.dtv_property_cache.inversion ^ i); + + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + //compute new dds_freq for the seg and adjust prbs + int seg_offset = + state->fe.dtv_property_cache.isdbt_sb_segment_idx - (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) - + (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2); + int clk = state->cfg.pll->internal; + u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) + int dds_offset = seg_offset * segtodds; + int new_dds, sub_channel; + if ((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even + dds_offset -= (int)(segtodds / 2); + + if (state->cfg.pll->ifreq == 0) { + if ((state->fe.dtv_property_cache.inversion ^ i) == 0) { + dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); + new_dds = dds_offset; + } else + new_dds = dds_offset; + + // We shift tuning frequency if the wanted segment is : + // - the segment of center frequency with an odd total number of segments + // - the segment to the left of center frequency with an even total number of segments + // - the segment to the right of center frequency with an even total number of segments + if ((state->fe.dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + && + (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + )) { + new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) + } + } else { + if ((state->fe.dtv_property_cache.inversion ^ i) == 0) + new_dds = state->cfg.pll->ifreq - dds_offset; + else + new_dds = state->cfg.pll->ifreq + dds_offset; + } + dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); + if (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) // if odd + sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; + else // if even + sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; + sub_channel -= 6; + + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K + || state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 + } else { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 + } + + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x423; + break; // 02~04 + case -4: + init_prbs = 0x9; + break; // 05~07 + case -3: + init_prbs = 0x5C7; + break; // 08~10 + case -2: + init_prbs = 0x7A6; + break; // 11~13 + case -1: + init_prbs = 0x3D8; + break; // 14~16 + case 0: + init_prbs = 0x527; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x79B; + break; // 23~25 + case 3: + init_prbs = 0x3D6; + break; // 26~28 + case 4: + init_prbs = 0x3A2; + break; // 29~31 + case 5: + init_prbs = 0x53B; + break; // 32~34 + case 6: + init_prbs = 0x2F4; + break; // 35~37 + default: + case 7: + init_prbs = 0x213; + break; // 38~40 + } + break; + + case TRANSMISSION_MODE_4K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x208; + break; // 02~04 + case -4: + init_prbs = 0xC3; + break; // 05~07 + case -3: + init_prbs = 0x7B9; + break; // 08~10 + case -2: + init_prbs = 0x423; + break; // 11~13 + case -1: + init_prbs = 0x5C7; + break; // 14~16 + case 0: + init_prbs = 0x3D8; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x3D6; + break; // 23~25 + case 3: + init_prbs = 0x53B; + break; // 26~28 + case 4: + init_prbs = 0x213; + break; // 29~31 + case 5: + init_prbs = 0x29; + break; // 32~34 + case 6: + init_prbs = 0xD0; + break; // 35~37 + default: + case 7: + init_prbs = 0x48E; + break; // 38~40 + } + break; + + default: + case TRANSMISSION_MODE_8K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x740; + break; // 02~04 + case -4: + init_prbs = 0x069; + break; // 05~07 + case -3: + init_prbs = 0x7DD; + break; // 08~10 + case -2: + init_prbs = 0x208; + break; // 11~13 + case -1: + init_prbs = 0x7B9; + break; // 14~16 + case 0: + init_prbs = 0x5C7; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x53B; + break; // 23~25 + case 3: + init_prbs = 0x29; + break; // 26~28 + case 4: + init_prbs = 0x48E; + break; // 29~31 + case 5: + init_prbs = 0x4C4; + break; // 32~34 + case 6: + init_prbs = 0x367; + break; // 33~37 + default: + case 7: + init_prbs = 0x684; + break; // 38~40 + } + break; + } + } else { // if not state->fe.dtv_property_cache.isdbt_sb_mode + dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); + } + /*P_mode == ?? */ + dib8000_write_word(state, 10, (seq << 4)); + // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); + + switch (state->fe.dtv_property_cache.guard_interval) { + case GUARD_INTERVAL_1_32: + guard = 0; + break; + case GUARD_INTERVAL_1_16: + guard = 1; + break; + case GUARD_INTERVAL_1_8: + guard = 2; + break; + case GUARD_INTERVAL_1_4: + default: + guard = 3; + break; + } + + dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 + + max_constellation = DQPSK; + for (i = 0; i < 3; i++) { + switch (state->fe.dtv_property_cache.layer[i].modulation) { + case DQPSK: + constellation = 0; + break; + case QPSK: + constellation = 1; + break; + case QAM_16: + constellation = 2; + break; + case QAM_64: + default: + constellation = 3; + break; + } + + switch (state->fe.dtv_property_cache.layer[i].fec) { + case FEC_1_2: + crate = 1; + break; + case FEC_2_3: + crate = 2; + break; + case FEC_3_4: + crate = 3; + break; + case FEC_5_6: + crate = 5; + break; + case FEC_7_8: + default: + crate = 7; + break; + } + + if ((state->fe.dtv_property_cache.layer[i].interleaving > 0) && + ((state->fe.dtv_property_cache.layer[i].interleaving <= 3) || + (state->fe.dtv_property_cache.layer[i].interleaving == 4 && state->fe.dtv_property_cache.isdbt_sb_mode == 1)) + ) + timeI = state->fe.dtv_property_cache.layer[i].interleaving; + else + timeI = 0; + dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe.dtv_property_cache.layer[i].segment_count & 0xf) << 6) | + (crate << 3) | timeI); + if (state->fe.dtv_property_cache.layer[i].segment_count > 0) { + switch (max_constellation) { + case DQPSK: + case QPSK: + if (state->fe.dtv_property_cache.layer[i].modulation == QAM_16 || + state->fe.dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + break; + case QAM_16: + if (state->fe.dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + break; + } + } + } + + mode = fft_to_mode(state); + + //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ + + dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | + ((state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe.dtv_property_cache. + isdbt_sb_mode & 1) << 4)); + + dprintk("mode = %d ; guard = %d", mode, state->fe.dtv_property_cache.guard_interval); + + /* signal optimization parameter */ + + if (state->fe.dtv_property_cache.isdbt_partial_reception) { + seg_diff_mask = (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; + for (i = 1; i < 3; i++) + nbseg_diff += + (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i + 1]; + } else { + for (i = 0; i < 3; i++) + nbseg_diff += + (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i]; + } + dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); + + state->differential_constellation = (seg_diff_mask != 0); + dib8000_set_diversity_in(&state->fe, state->diversity_onoff); + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + seg_mask13 = 0x00E0; + else // 1-segment + seg_mask13 = 0x0040; + } else + seg_mask13 = 0x1fff; + + // WRITE: Mode & Diff mask + dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); + + if ((seg_diff_mask) || (state->fe.dtv_property_cache.isdbt_sb_mode)) + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + else + dib8000_write_word(state, 268, (2 << 9) | 39); //init value + + // ---- SMALL ---- + // P_small_seg_diff + dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 + + dib8000_write_word(state, 353, seg_mask13); // ADDR 353 + +/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ + // dib8000_write_word(state, 351, (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 ); + + // ---- SMALL ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_2k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_2k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg_0dqpsk; + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + ncoeff = coeff_2k_sb_3seg_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg; + } + } + break; + + case TRANSMISSION_MODE_4K: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_4k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_4k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg_0dqpsk; + } + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_4k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg; + } + } + break; + + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + default: + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + ncoeff = coeff_8k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_8k_sb_1seg; + } else { // 3-segments + if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg_0dqpsk; + } + } else { // QPSK or QAM on central segment + if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + ncoeff = coeff_8k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg; + } + } + break; + } + } + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + for (i = 0; i < 8; i++) + dib8000_write_word(state, 343 + i, ncoeff[i]); + + // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 + dib8000_write_word(state, 351, + (state->fe.dtv_property_cache.isdbt_sb_mode << 9) | (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); + + // ---- COFF ---- + // Carloff, the most robust + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots + + // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 + // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 + dib8000_write_word(state, 187, + (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 2) + | 0x3); + +/* // P_small_coef_ext_enable = 1 */ +/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ + + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + + // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) + if (mode == 3) + dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); + else + dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); + // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 300); + dib8000_write_word(state, 182, 150); + dib8000_write_word(state, 183, 80); + dib8000_write_word(state, 184, 300); + dib8000_write_word(state, 185, 150); + dib8000_write_word(state, 186, 80); + } else { // Sound Broadcasting mode 3 seg + // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 + /* if (mode == 3) */ + /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ + /* else */ + /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ + dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); + + // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 350); + dib8000_write_word(state, 182, 300); + dib8000_write_word(state, 183, 250); + dib8000_write_word(state, 184, 350); + dib8000_write_word(state, 185, 300); + dib8000_write_word(state, 186, 250); + } + + } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments + dib8000_write_word(state, 180, (16 << 6) | 9); + dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); + coff_pow = 0x2800; + for (i = 0; i < 6; i++) + dib8000_write_word(state, 181 + i, coff_pow); + + // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + + // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 + dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); + // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + } + // ---- FFT ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 && state->fe.dtv_property_cache.isdbt_partial_reception == 0) // 1-seg + dib8000_write_word(state, 178, 64); // P_fft_powrange=64 + else + dib8000_write_word(state, 178, 32); // P_fft_powrange=32 + + /* make the cpil_coff_lock more robust but slower p_coff_winlen + * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) + */ + /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) + dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ + + dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ + dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ + dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ + if ((!state->fe.dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ + else + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ + dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ + //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ + if (!autosearching) + dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ + else + dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. + dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); + + dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ + + /* offset loop parameters */ + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); + + else // Sound Broadcasting mode 3 seg + /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); + } else + // TODO in 13 seg, timf_alpha can always be the same or not ? + /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); + + else // Sound Broadcasting mode 3 seg + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); + } else + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); + + /* P_dvsy_sync_wait - reuse mode */ + switch (state->fe.dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_8K: + mode = 256; + break; + case TRANSMISSION_MODE_4K: + mode = 128; + break; + default: + case TRANSMISSION_MODE_2K: + mode = 64; + break; + } + if (state->cfg.diversity_delay == 0) + mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo + else + mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo + mode <<= 4; + dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); + + /* channel estimation fine configuration */ + switch (max_constellation) { + case QAM_64: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 + break; + case QAM_16: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) + break; + default: + ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level + coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (mode = 0; mode < 4; mode++) + dib8000_write_word(state, 215 + mode, coeff[mode]); + + // update ana_gain depending on max constellation + dib8000_write_word(state, 116, ana_gain); + // update ADC target depending on ana_gain + if (ana_gain) { // set -16dB ADC target for ana_gain=-1 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i]); + } else { // set -22dB ADC target for ana_gain=0 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); + } + + // ---- ANA_FE ---- + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + ana_fe = ana_fe_coeff_3seg; + else // 1-segment + ana_fe = ana_fe_coeff_1seg; + } else + ana_fe = ana_fe_coeff_13seg; + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) + for (mode = 0; mode < 24; mode++) + dib8000_write_word(state, 117 + mode, ana_fe[mode]); + + // ---- CHAN_BLK ---- + for (i = 0; i < 13; i++) { + if ((((~seg_diff_mask) >> i) & 1) == 1) { + P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); + P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); + } + } + dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge + dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge + // "P_cspu_left_edge" not used => do not care + // "P_cspu_right_edge" not used => do not care + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 + dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0 // 1-segment + && state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { + //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 + dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 + } + } else if (state->isdbt_cfg_loaded == 0) { + dib8000_write_word(state, 228, 0); // default value + dib8000_write_word(state, 265, 31); // default value + dib8000_write_word(state, 205, 0x200f); // init value + } + // ---- TMCC ---- + for (i = 0; i < 3; i++) + tmcc_pow += + (((state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe.dtv_property_cache.layer[i].segment_count); + // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); + // Threshold is set at 1/4 of max power. + tmcc_pow *= (1 << (9 - 2)); + + dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k + dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k + dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k + //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); + // ---- PHA3 ---- + + if (state->isdbt_cfg_loaded == 0) + dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ + + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + state->isdbt_cfg_loaded = 0; + else + state->isdbt_cfg_loaded = 1; + +} + +static int dib8000_autosearch_start(struct dvb_frontend *fe) +{ + u8 factor; + u32 value; + struct dib8000_state *state = fe->demodulator_priv; + + int slist = 0; + + state->fe.dtv_property_cache.inversion = 0; + if (!state->fe.dtv_property_cache.isdbt_sb_mode) + state->fe.dtv_property_cache.layer[0].segment_count = 13; + state->fe.dtv_property_cache.layer[0].modulation = QAM_64; + state->fe.dtv_property_cache.layer[0].fec = FEC_2_3; + state->fe.dtv_property_cache.layer[0].interleaving = 0; + + //choose the right list, in sb, always do everything + if (state->fe.dtv_property_cache.isdbt_sb_mode) { + state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); + } else { + if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 + } else + slist = 3; + } else { + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 2; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + } else + slist = 0; + } + + if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) + state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) + state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + + dprintk("using list for autosearch : %d", slist); + dib8000_set_channel(state, (unsigned char)slist, 1); + //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + + factor = 1; + + //set lock_mask values + dib8000_write_word(state, 6, 0x4); + dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 8, 0x1000); + + //set lock_mask wait time values + value = 50 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time + value = 1000 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time + + value = dib8000_read_word(state, 0); + dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); + dib8000_read_word(state, 1284); // reset the INT. n_irq_pending + dib8000_write_word(state, 0, (u16) value); + + } + + return 0; +} + +static int dib8000_autosearch_irq(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 irq_pending = dib8000_read_word(state, 1284); + + if (irq_pending & 0x1) { // failed + dprintk("dib8000_autosearch_irq failed"); + return 1; + } + + if (irq_pending & 0x2) { // succeeded + dprintk("dib8000_autosearch_irq succeeded"); + return 2; + } + + return 0; // still pending +} + +static int dib8000_tune(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + int ret = 0; + u16 value, mode = fft_to_mode(state); + + // we are already tuned - just resuming from suspend + if (state == NULL) + return -EINVAL; + + dib8000_set_bandwidth(state, state->fe.dtv_property_cache.bandwidth_hz / 1000); + dib8000_set_channel(state, 0, 0); + + // restart demod + ret |= dib8000_write_word(state, 770, 0x4000); + ret |= dib8000_write_word(state, 770, 0x0000); + msleep(45); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ + /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ + + // never achieved a lock before - wait for timfreq to update + if (state->timf == 0) { + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + msleep(300); + else // Sound Broadcasting mode 3 seg + msleep(500); + } else // 13 seg + msleep(200); + } + //dump_reg(state); + if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + + /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ + dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); + //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); + + /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ + ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); + + } else { // Sound Broadcasting mode 3 seg + + /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ + dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); + + ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); + } + + } else { // 13 seg + /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); + + ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); + + } + + // we achieved a coff_cpil_lock - it's time to update the timf + if ((dib8000_read_word(state, 568) >> 11) & 0x1) + dib8000_update_timf(state); + + //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start + dib8000_write_word(state, 6, 0x200); + + if (state->revision == 0x8002) { + value = dib8000_read_word(state, 903); + dib8000_write_word(state, 903, value & ~(1 << 3)); + msleep(1); + dib8000_write_word(state, 903, value | (1 << 3)); + } + + return ret; +} + +static int dib8000_wakeup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + dib8000_set_power_mode(state, DIB8000M_POWER_ALL); + dib8000_set_adc_state(state, DIBX000_ADC_ON); + if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("could not start Slow ADC"); + + return 0; +} + +static int dib8000_sleep(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + if (1) { + dib8000_set_output_mode(st, OUTMODE_HIGH_Z); + dib8000_set_power_mode(st, DIB8000M_POWER_INTERFACE_ONLY); + return dib8000_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(st, DIBX000_ADC_OFF); + } else { + + return 0; + } +} + +static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 i, val = 0; + + fe->dtv_property_cache.bandwidth_hz = 6000000; + + fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; + + val = dib8000_read_word(state, 570); + fe->dtv_property_cache.inversion = (val & 0x40) >> 6; + switch ((val & 0x30) >> 4) { + case 1: + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 3: + default: + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch (val & 0x3) { + case 0: + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; + dprintk("dib8000_get_frontend GI = 1/32 "); + break; + case 1: + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; + dprintk("dib8000_get_frontend GI = 1/16 "); + break; + case 2: + dprintk("dib8000_get_frontend GI = 1/8 "); + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + dprintk("dib8000_get_frontend GI = 1/4 "); + fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; + break; + } + + val = dib8000_read_word(state, 505); + fe->dtv_property_cache.isdbt_partial_reception = val & 1; + dprintk("dib8000_get_frontend : partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); + + for (i = 0; i < 3; i++) { + val = dib8000_read_word(state, 493 + i); + fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; + dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); + + val = dib8000_read_word(state, 499 + i); + fe->dtv_property_cache.layer[i].interleaving = val & 0x3; + dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving); + + val = dib8000_read_word(state, 481 + i); + switch (val & 0x7) { + case 1: + fe->dtv_property_cache.layer[i].fec = FEC_1_2; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 1/2 ", i); + break; + case 2: + fe->dtv_property_cache.layer[i].fec = FEC_2_3; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 2/3 ", i); + break; + case 3: + fe->dtv_property_cache.layer[i].fec = FEC_3_4; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 3/4 ", i); + break; + case 5: + fe->dtv_property_cache.layer[i].fec = FEC_5_6; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 5/6 ", i); + break; + default: + fe->dtv_property_cache.layer[i].fec = FEC_7_8; + dprintk("dib8000_get_frontend : Layer %d Code Rate = 7/8 ", i); + break; + } + + val = dib8000_read_word(state, 487 + i); + switch (val & 0x3) { + case 0: + dprintk("dib8000_get_frontend : Layer %d DQPSK ", i); + fe->dtv_property_cache.layer[i].modulation = DQPSK; + break; + case 1: + fe->dtv_property_cache.layer[i].modulation = QPSK; + dprintk("dib8000_get_frontend : Layer %d QPSK ", i); + break; + case 2: + fe->dtv_property_cache.layer[i].modulation = QAM_16; + dprintk("dib8000_get_frontend : Layer %d QAM16 ", i); + break; + case 3: + default: + dprintk("dib8000_get_frontend : Layer %d QAM64 ", i); + fe->dtv_property_cache.layer[i].modulation = QAM_64; + break; + } + } + return 0; +} + +static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib8000_state *state = fe->demodulator_priv; + int time, ret; + + dib8000_set_output_mode(state, OUTMODE_HIGH_Z); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + /* start up the AGC */ + state->tune_state = CT_AGC_START; + do { + time = dib8000_agc_startup(fe); + if (time != FE_CALLBACK_TIME_NEVER) + msleep(time / 10); + else + break; + } while (state->tune_state != CT_AGC_STOP); + + if (state->fe.dtv_property_cache.frequency == 0) { + dprintk("dib8000: must at least specify frequency "); + return 0; + } + + if (state->fe.dtv_property_cache.bandwidth_hz == 0) { + dprintk("dib8000: no bandwidth specified, set to default "); + state->fe.dtv_property_cache.bandwidth_hz = 6000000; + } + + state->tune_state = CT_DEMOD_START; + + if ((state->fe.dtv_property_cache.delivery_system != SYS_ISDBT) || + (state->fe.dtv_property_cache.inversion == INVERSION_AUTO) || + (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || + (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && + (state->fe.dtv_property_cache.layer[0].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[0].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[0].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[0].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && + (state->fe.dtv_property_cache.layer[1].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[1].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[1].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[1].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && + (state->fe.dtv_property_cache.layer[2].segment_count != 0xff) && + (state->fe.dtv_property_cache.layer[2].segment_count != 0) && + ((state->fe.dtv_property_cache.layer[2].modulation == QAM_AUTO) || + (state->fe.dtv_property_cache.layer[2].fec == FEC_AUTO))) || + (((state->fe.dtv_property_cache.layer[0].segment_count == 0) || + ((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && + ((state->fe.dtv_property_cache.layer[1].segment_count == 0) || + ((state->fe.dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && + ((state->fe.dtv_property_cache.layer[2].segment_count == 0) || ((state->fe.dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { + int i = 800, found; + + dib8000_set_bandwidth(state, fe->dtv_property_cache.bandwidth_hz / 1000); + dib8000_autosearch_start(fe); + do { + msleep(10); + found = dib8000_autosearch_irq(fe); + } while (found == 0 && i--); + + dprintk("Frequency %d Hz, autosearch returns: %d", fep->frequency, found); + + if (found == 0 || found == 1) + return 0; // no channel found + + dib8000_get_frontend(fe, fep); + } + + ret = dib8000_tune(fe); + + /* make this a config parameter */ + dib8000_set_output_mode(state, state->cfg.output_mode); + + return ret; +} + +static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 lock = dib8000_read_word(state, 568); + + *stat = 0; + + if ((lock >> 14) & 1) // AGC + *stat |= FE_HAS_SIGNAL; + + if ((lock >> 8) & 1) // Equal + *stat |= FE_HAS_CARRIER; + + if ((lock >> 3) & 1) // TMCC_SYNC + *stat |= FE_HAS_SYNC; + + if ((lock >> 5) & 7) // FEC MPEG + *stat |= FE_HAS_LOCK; + + lock = dib8000_read_word(state, 554); // Viterbi Layer A + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 555); // Viterbi Layer B + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 556); // Viterbi Layer C + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + return 0; +} + +static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib8000_state *state = fe->demodulator_priv; + *ber = (dib8000_read_word(state, 560) << 16) | dib8000_read_word(state, 561); // 13 segments + return 0; +} + +static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib8000_state *state = fe->demodulator_priv; + *unc = dib8000_read_word(state, 565); // packet error on 13 seg + return 0; +} + +static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 val = dib8000_read_word(state, 390); + *strength = 65535 - val; + return 0; +} + +static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 val; + s32 signal_mant, signal_exp, noise_mant, noise_exp; + u32 result = 0; + + val = dib8000_read_word(state, 542); + noise_mant = (val >> 6) & 0xff; + noise_exp = (val & 0x3f); + + val = dib8000_read_word(state, 543); + signal_mant = (val >> 6) & 0xff; + signal_exp = (val & 0x3f); + + if ((noise_exp & 0x20) != 0) + noise_exp -= 0x40; + if ((signal_exp & 0x20) != 0) + signal_exp -= 0x40; + + if (signal_mant != 0) + result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); + else + result = intlog10(2) * 10 * signal_exp - 100; + if (noise_mant != 0) + result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); + else + result -= intlog10(2) * 10 * noise_exp - 100; + + *snr = result / (1 << 24); + return 0; +} + +int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + int k = 0; + u8 new_addr = 0; + struct i2c_device client = {.adap = host }; + + for (k = no_of_demods - 1; k >= 0; k--) { + /* designated i2c address */ + new_addr = first_addr + (k << 1); + + client.addr = new_addr; + dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ + if (dib8000_identify(&client) == 0) { + dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ + client.addr = default_addr; + if (dib8000_identify(&client) == 0) { + dprintk("#%d: not identified", k); + return -EINVAL; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6)); + + /* set new i2c address and force divstart */ + dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2); + client.addr = new_addr; + dib8000_identify(&client); + + dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + new_addr = first_addr | (k << 1); + client.addr = new_addr; + + // unforce divstr + dib8000_i2c_write16(&client, 1285, new_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, 0); + } + + return 0; +} + +EXPORT_SYMBOL(dib8000_i2c_enumeration); +static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + tune->step_size = 0; + tune->max_drift = 0; + return 0; +} + +static void dib8000_release(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + struct dib8000_state *st = fe->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} + +EXPORT_SYMBOL(dib8000_get_i2c_master); + +static const struct dvb_frontend_ops dib8000_ops = { + .info = { + .name = "DiBcom 8000 ISDB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib8000_release, + + .init = dib8000_wakeup, + .sleep = dib8000_sleep, + + .set_frontend = dib8000_set_frontend, + .get_tune_settings = dib8000_fe_get_tune_settings, + .get_frontend = dib8000_get_frontend, + + .read_status = dib8000_read_status, + .read_ber = dib8000_read_ber, + .read_signal_strength = dib8000_read_signal_strength, + .read_snr = dib8000_read_snr, + .read_ucblocks = dib8000_read_unc_blocks, +}; + +struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +{ + struct dvb_frontend *fe; + struct dib8000_state *state; + + dprintk("dib8000_attach"); + + state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); + state->i2c.adap = i2c_adap; + state->i2c.addr = i2c_addr; + state->gpio_val = cfg->gpio_val; + state->gpio_dir = cfg->gpio_dir; + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + state->cfg.output_mode = OUTMODE_MPEG2_FIFO; + + fe = &state->fe; + fe->demodulator_priv = state; + memcpy(&state->fe.ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); + + state->timf_default = cfg->pll->timf; + + if (dib8000_identify(&state->i2c) == 0) + goto error; + + dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr); + + dib8000_reset(fe); + + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ + + return fe; + + error: + kfree(state); + return NULL; +} + +EXPORT_SYMBOL(dib8000_attach); + +MODULE_AUTHOR("Olivier Grenie <Olivier.Grenie@dibcom.fr, " "Patrick Boettcher <pboettcher@dibcom.fr>"); +MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h new file mode 100644 index 00000000000..a86de340dd5 --- /dev/null +++ b/drivers/media/dvb/frontends/dib8000.h @@ -0,0 +1,79 @@ +#ifndef DIB8000_H +#define DIB8000_H + +#include "dibx000_common.h" + +struct dib8000_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *pll; + +#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB8000_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + u16 pwm_freq_div; + + void (*agc_control) (struct dvb_frontend *, u8 before); + + u16 drives; + u16 diversity_delay; + u8 div_cfg; + u8 output_mode; + u8 refclksel; +}; + +#define DEFAULT_DIB8000_I2C_ADDRESS 18 + +#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); + +extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); + +extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); +extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); +#else +static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index 315e09e95b0..4efca30d212 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -15,29 +15,31 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) (val >> 8) & 0xff, val & 0xff, }; struct i2c_msg msg = { - .addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4 + .addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4 }; return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } -static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf) +static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf) { if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { - dprintk("selecting interface: %d\n",intf); + dprintk("selecting interface: %d\n", intf); mst->selected_interface = intf; return dibx000_write_word(mst, mst->base_reg + 4, intf); } return 0; } -static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff) +static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], + u8 addr, int onoff) { u16 val; if (onoff) - val = addr << 8; // bit 7 = use master or not, if 0, the gate is open + val = addr << 8; // bit 7 = use master or not, if 0, the gate is open else val = 1 << 7; @@ -45,7 +47,7 @@ static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 ad val <<= 1; tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); - tx[1] = ( (mst->base_reg + 1) & 0xff); + tx[1] = ((mst->base_reg + 1) & 0xff); tx[2] = val >> 8; tx[3] = val & 0xff; @@ -57,59 +59,78 @@ static u32 dibx000_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C; } -static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) { struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); struct i2c_msg m[2 + num]; u8 tx_open[4], tx_close[4]; - memset(m,0, sizeof(struct i2c_msg) * (2 + num)); + memset(m, 0, sizeof(struct i2c_msg) * (2 + num)); dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); - dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); + dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); m[0].addr = mst->i2c_addr; - m[0].buf = tx_open; - m[0].len = 4; + m[0].buf = tx_open; + m[0].len = 4; memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0); - m[num+1].addr = mst->i2c_addr; - m[num+1].buf = tx_close; - m[num+1].len = 4; + m[num + 1].addr = mst->i2c_addr; + m[num + 1].buf = tx_close; + m[num + 1].len = 4; - return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO; + return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO; } static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { - .master_xfer = dibx000_i2c_gated_tuner_xfer, + .master_xfer = dibx000_i2c_gated_tuner_xfer, .functionality = dibx000_i2c_func, }; -struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating) +struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf, + int gating) { struct i2c_adapter *i2c = NULL; switch (intf) { - case DIBX000_I2C_INTERFACE_TUNER: - if (gating) - i2c = &mst->gated_tuner_i2c_adap; - break; - default: - printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); - break; + case DIBX000_I2C_INTERFACE_TUNER: + if (gating) + i2c = &mst->gated_tuner_i2c_adap; + break; + default: + printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); + break; } return i2c; } + EXPORT_SYMBOL(dibx000_get_i2c_adapter); -static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char *name, struct dibx000_i2c_master *mst) +void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) +{ + /* initialize the i2c-master by closing the gate */ + u8 tx[4]; + struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; + + dibx000_i2c_gate_ctrl(mst, tx, 0, 0); + i2c_transfer(mst->i2c_adap, &m, 1); + mst->selected_interface = 0xff; // the first time force a select of the I2C + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); +} + +EXPORT_SYMBOL(dibx000_reset_i2c_master); + +static int i2c_adapter_init(struct i2c_adapter *i2c_adap, + struct i2c_algorithm *algo, const char *name, + struct dibx000_i2c_master *mst) { strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); - i2c_adap->class = I2C_CLASS_TV_DIGITAL, - i2c_adap->algo = algo; + i2c_adap->class = I2C_CLASS_TV_DIGITAL, i2c_adap->algo = algo; i2c_adap->algo_data = NULL; i2c_set_adapdata(i2c_adap, mst); if (i2c_add_adapter(i2c_adap) < 0) @@ -117,34 +138,40 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm * return 0; } -int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr) +int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, + struct i2c_adapter *i2c_adap, u8 i2c_addr) { u8 tx[4]; - struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 }; + struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 }; mst->device_rev = device_rev; - mst->i2c_adap = i2c_adap; - mst->i2c_addr = i2c_addr >> 1; + mst->i2c_adap = i2c_adap; + mst->i2c_addr = i2c_addr >> 1; - if (device_rev == DIB7000P) + if (device_rev == DIB7000P || device_rev == DIB8000) mst->base_reg = 1024; else mst->base_reg = 768; - if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0) - printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n"); + if (i2c_adapter_init + (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, + "DiBX000 tuner I2C bus", mst) != 0) + printk(KERN_ERR + "DiBX000: could not initialize the tuner i2c_adapter\n"); /* initialize the i2c-master by closing the gate */ dibx000_i2c_gate_ctrl(mst, tx, 0, 0); return i2c_transfer(i2c_adap, &m, 1) == 1; } + EXPORT_SYMBOL(dibx000_init_i2c_master); void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) { i2c_del_adapter(&mst->gated_tuner_i2c_adap); } + EXPORT_SYMBOL(dibx000_exit_i2c_master); MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index 84e4d536292..5be10eca07c 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -2,7 +2,7 @@ #define DIBX000_COMMON_H enum dibx000_i2c_interface { - DIBX000_I2C_INTERFACE_TUNER = 0, + DIBX000_I2C_INTERFACE_TUNER = 0, DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, DIBX000_I2C_INTERFACE_GPIO_3_4 = 2 }; @@ -12,22 +12,29 @@ struct dibx000_i2c_master { #define DIB7000 2 #define DIB7000P 11 #define DIB7000MC 12 +#define DIB8000 13 u16 device_rev; enum dibx000_i2c_interface selected_interface; -// struct i2c_adapter tuner_i2c_adap; - struct i2c_adapter gated_tuner_i2c_adap; +// struct i2c_adapter tuner_i2c_adap; + struct i2c_adapter gated_tuner_i2c_adap; struct i2c_adapter *i2c_adap; - u8 i2c_addr; + u8 i2c_addr; u16 base_reg; }; -extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr); -extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating); +extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, + u16 device_rev, struct i2c_adapter *i2c_adap, + u8 i2c_addr); +extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master + *mst, + enum dibx000_i2c_interface + intf, int gating); extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); +extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); #define BAND_LBAND 0x01 #define BAND_UHF 0x02 @@ -41,18 +48,18 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) struct dibx000_agc_config { - /* defines the capabilities of this AGC-setting - using the BAND_-defines*/ - u8 band_caps; + /* defines the capabilities of this AGC-setting - using the BAND_-defines */ + u8 band_caps; u16 setup; u16 inv_gain; u16 time_stabiliz; - u8 alpha_level; + u8 alpha_level; u16 thlock; - u8 wbd_inv; + u8 wbd_inv; u16 wbd_ref; u8 wbd_sel; u8 wbd_alpha; @@ -92,8 +99,8 @@ struct dibx000_agc_config { }; struct dibx000_bandwidth_config { - u32 internal; - u32 sampling; + u32 internal; + u32 sampling; u8 pll_prediv; u8 pll_ratio; diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 9f6349964cd..6d865d6161d 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -389,6 +389,77 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { } }; +/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ +static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { + .name = "Samsung TDTC9251DH0", + .min = 48000000, + .max = 863000000, + .iffreq = 36166667, + .count = 3, + .entries = { + { 157500000, 166667, 0xcc, 0x09 }, + { 443000000, 166667, 0xcc, 0x0a }, + { 863000000, 166667, 0xcc, 0x08 }, + } +}; + +/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ +static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { + .name = "Samsung TBDU18132", + .min = 950000, + .max = 2150000, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1550000, 125, 0x84, 0x82 }, + { 4095937, 125, 0x84, 0x80 }, + } + /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported + * by this driver. The two extra bits are 0x60 in the third byte. 15 + * bits is enough for over 4 GHz, which is enough to cover the range + * of this tuner. We could use the additional divisor bits by adding + * more entries, e.g. + { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, }, + { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, }, + { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ +}; + +/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ +static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { + .name = "Samsung TBMU24112", + .min = 950000, + .max = 2150000, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1500000, 125, 0x84, 0x18 }, + { 9999999, 125, 0x84, 0x08 }, + } +}; + +/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */ +/* byte 4 : 1 * * AGD R3 R2 R1 R0 + * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 + * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 + * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 + * 47 - 153 0 * 0 0 0 0 0 1 0x01 + * 153 - 430 0 * 0 0 0 0 1 0 0x02 + * 430 - 822 0 * 0 0 1 0 0 0 0x08 + * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ +static struct dvb_pll_desc dvb_pll_alps_tdee4 = { + .name = "ALPS TDEE4", + .min = 47000000, + .max = 862000000, + .iffreq = 36125000, + .count = 4, + .entries = { + { 153000000, 62500, 0x95, 0x01 }, + { 430000000, 62500, 0x95, 0x02 }, + { 822000000, 62500, 0x95, 0x08 }, + { 999999999, 62500, 0x95, 0x88 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -402,11 +473,15 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_TUA6034] = &dvb_pll_tua6034, [DVB_PLL_TDA665X] = &dvb_pll_tda665x, [DVB_PLL_TDED4] = &dvb_pll_tded4, + [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4, [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, [DVB_PLL_OPERA1] = &dvb_pll_opera1, [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, + [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, + [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, + [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 05239f579cc..086964344c3 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -23,6 +23,10 @@ #define DVB_PLL_PHILIPS_SD1878_TDA8261 12 #define DVB_PLL_OPERA1 13 #define DVB_PLL_SAMSUNG_DTOS403IH102A 14 +#define DVB_PLL_SAMSUNG_TDTC9251DH0 15 +#define DVB_PLL_SAMSUNG_TBDU18132 16 +#define DVB_PLL_SAMSUNG_TBMU24112 17 +#define DVB_PLL_TDEE4 18 /** * Attach a dvb-pll to the supplied frontend structure. diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c index db8a937cc63..a7fc7e53a55 100644 --- a/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c index e1e70e9e0cb..3051b64aa17 100644 --- a/drivers/media/dvb/frontends/l64781.c +++ b/drivers/media/dvb/frontends/l64781.c @@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config, { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL); + state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c index eb72a9866c9..e334b5d4e57 100644 --- a/drivers/media/dvb/frontends/lgdt3304.c +++ b/drivers/media/dvb/frontends/lgdt3304.c @@ -363,6 +363,8 @@ struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, struct lgdt3304_state *state; state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); + if (state == NULL) + return NULL; state->addr = config->i2c_address; state->i2c = i2c; diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c index 855852fddf2..bb37ed289a0 100644 --- a/drivers/media/dvb/frontends/lgs8gl5.c +++ b/drivers/media/dvb/frontends/lgs8gl5.c @@ -387,7 +387,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) dprintk("%s\n", __func__); /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); + state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c index fde27645bbe..eabcadc425d 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.c +++ b/drivers/media/dvb/frontends/lgs8gxx.c @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644); MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." "Signal strength calculation is slow.(default:on)."); +static const u8 lgs8g75_initdat[] = { + 0x01, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0xF5, 0xA8, 0xF5, 0xB8, 0xF5, 0x88, 0xF5, + 0x89, 0xF5, 0x87, 0x75, 0xD0, 0x00, 0x11, 0x50, + 0x11, 0x50, 0xF4, 0xF5, 0x80, 0xF5, 0x90, 0xF5, + 0xA0, 0xF5, 0xB0, 0x75, 0x81, 0x30, 0x80, 0x01, + 0x32, 0x90, 0x80, 0x12, 0x74, 0xFF, 0xF0, 0x90, + 0x80, 0x13, 0x74, 0x1F, 0xF0, 0x90, 0x80, 0x23, + 0x74, 0x01, 0xF0, 0x90, 0x80, 0x22, 0xF0, 0x90, + 0x00, 0x48, 0x74, 0x00, 0xF0, 0x90, 0x80, 0x4D, + 0x74, 0x05, 0xF0, 0x90, 0x80, 0x09, 0xE0, 0x60, + 0x21, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x1B, 0x12, + 0x00, 0xDD, 0x14, 0x60, 0x15, 0x12, 0x00, 0xDD, + 0x14, 0x60, 0x0F, 0x12, 0x00, 0xDD, 0x14, 0x60, + 0x09, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x03, 0x12, + 0x00, 0xDD, 0x90, 0x80, 0x42, 0xE0, 0x60, 0x0B, + 0x14, 0x60, 0x0C, 0x14, 0x60, 0x0D, 0x14, 0x60, + 0x0E, 0x01, 0xB3, 0x74, 0x04, 0x01, 0xB9, 0x74, + 0x05, 0x01, 0xB9, 0x74, 0x07, 0x01, 0xB9, 0x74, + 0x0A, 0xC0, 0xE0, 0x74, 0xC8, 0x12, 0x00, 0xE2, + 0xD0, 0xE0, 0x14, 0x70, 0xF4, 0x90, 0x80, 0x09, + 0xE0, 0x70, 0xAE, 0x12, 0x00, 0xF6, 0x12, 0x00, + 0xFE, 0x90, 0x00, 0x48, 0xE0, 0x04, 0xF0, 0x90, + 0x80, 0x4E, 0xF0, 0x01, 0x73, 0x90, 0x80, 0x08, + 0xF0, 0x22, 0xF8, 0x7A, 0x0C, 0x79, 0xFD, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, + 0xF6, 0xDA, 0xF2, 0xD8, 0xEE, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x54, 0xFD, 0xF0, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x44, 0xC2, 0xF0, 0x22 +}; + /* LGS8GXX internal helper functions */ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) @@ -55,7 +91,7 @@ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; msg.addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) msg.addr += 0x02; if (debug >= 2) @@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) }; dev_addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) dev_addr += 0x02; msg[1].addr = msg[0].addr = dev_addr; @@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) return 0; } +static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, + u8 val, u8 delay, u8 tries) +{ + u8 t; + int i; + + for (i = 0; i < tries; i++) { + lgs8gxx_read_reg(priv, reg, &t); + + if ((t & mask) == val) + return 0; + msleep(delay); + } + + return 1; +} + static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) { const struct lgs8gxx_config *config = priv->config; u8 if_conf; - if_conf = 0x10; /* AGC output on; */ + if_conf = 0x10; /* AGC output on, RF_AGC output off; */ if_conf |= ((config->ext_adc) ? 0x80 : 0x00) | ((config->if_neg_center) ? 0x04 : 0x00) | ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ - ((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) | - ((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00); + ((config->adc_signed) ? 0x02 : 0x00) | + ((config->if_neg_edge) ? 0x01 : 0x00); if (config->ext_adc && (config->prod == LGS8GXX_PROD_LGS8G52)) { @@ -157,39 +210,82 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) } dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); - lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); - lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); - lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); - lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); + } else { + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + } + + return 0; +} + +static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) +{ + u64 val; + u32 v32 = 0; + u8 reg_addr, t; + int i; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + reg_addr = 0x23; + else + reg_addr = 0x48; + + for (i = 0; i < 4; i++) { + lgs8gxx_read_reg(priv, reg_addr, &t); + v32 <<= 8; + v32 |= t; + reg_addr--; + } + val = v32; + val *= priv->config->if_clk_freq; + val /= (u64)1 << 32; + dprintk("AFC = %u kHz\n", (u32)val); return 0; } static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) { u8 t; + u8 prod = priv->config->prod; - if (priv->config->prod == LGS8GXX_PROD_LGS8913) + if (prod == LGS8GXX_PROD_LGS8913) lgs8gxx_write_reg(priv, 0xC6, 0x01); - lgs8gxx_read_reg(priv, 0x7E, &t); - lgs8gxx_write_reg(priv, 0x7E, t | 0x01); - - /* clear FEC self reset */ - lgs8gxx_read_reg(priv, 0xC5, &t); - lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + if (prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x04); + lgs8gxx_write_reg(priv, 0x0C, t | 0x80); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } else if (prod == LGS8GXX_PROD_LGS8913 || + prod == LGS8GXX_PROD_LGS8GL5 || + prod == LGS8GXX_PROD_LGS8G42 || + prod == LGS8GXX_PROD_LGS8G52 || + prod == LGS8GXX_PROD_LGS8G54) { + lgs8gxx_read_reg(priv, 0x7E, &t); + lgs8gxx_write_reg(priv, 0x7E, t | 0x01); + + /* clear FEC self reset */ + lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + } - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + if (prod == LGS8GXX_PROD_LGS8913) { /* FEC auto detect */ lgs8gxx_write_reg(priv, 0xC1, 0x03); lgs8gxx_read_reg(priv, 0x7C, &t); t = (t & 0x8C) | 0x03; lgs8gxx_write_reg(priv, 0x7C, t); - } - - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { /* BER test mode */ lgs8gxx_read_reg(priv, 0xC3, &t); t = (t & 0xEF) | 0x10; @@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) int ret = 0; u8 t; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t2; + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x80); + lgs8gxx_write_reg(priv, 0x0C, t); + + lgs8gxx_read_reg(priv, 0x0C, &t); + lgs8gxx_read_reg(priv, 0x19, &t2); + + if (((t&0x03) == 0x01) && (t2&0x01)) { + lgs8gxx_write_reg(priv, 0x6E, 0x05); + lgs8gxx_write_reg(priv, 0x39, 0x02); + lgs8gxx_write_reg(priv, 0x39, 0x03); + lgs8gxx_write_reg(priv, 0x3D, 0x05); + lgs8gxx_write_reg(priv, 0x3E, 0x28); + lgs8gxx_write_reg(priv, 0x53, 0x80); + } else { + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } + + lgs8gxx_soft_reset(priv); + return 0; + } + /* turn off auto-detect; manual settings */ lgs8gxx_write_reg(priv, 0x7E, 0); if (priv->config->prod == LGS8GXX_PROD_LGS8913) @@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) int ret = 0; u8 t; - ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + ret = lgs8gxx_read_reg(priv, 0x13, &t); + else + ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) return ret; - *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + *locked = ((t & 0x80) == 0x80) ? 1 : 0; + else + *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + return 0; +} + +/* Wait for Code Acquisition Lock */ +static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 reg, mask, val; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x13; + mask = 0x80; + val = 0x80; + } else { + reg = 0x4B; + mask = 0xC0; + val = 0xC0; + } + + ret = wait_reg_mask(priv, reg, mask, val, 50, 40); + *locked = (ret == 0) ? 1 : 0; + return 0; } @@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, u8 *finished) { int ret = 0; - u8 t; + u8 reg, mask, val; - ret = lgs8gxx_read_reg(priv, 0xA4, &t); - if (ret != 0) - return ret; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x1f; + mask = 0xC0; + val = 0x80; + } else { + reg = 0xA4; + mask = 0x03; + val = 0x01; + } - *finished = ((t & 0x3) == 0x1) ? 1 : 0; + ret = wait_reg_mask(priv, reg, mask, val, 10, 20); + *finished = (ret == 0) ? 1 : 0; return 0; } -static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) +static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, + u8 *locked) { - int err; + int err = 0; u8 ad_fini = 0; + u8 t1, t2; if (gi == GI_945) dprintk("try GI 945\n"); @@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) dprintk("try GI 595\n"); else if (gi == GI_420) dprintk("try GI 420\n"); - lgs8gxx_write_reg(priv, 0x04, gi); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t1); + lgs8gxx_read_reg(priv, 0x18, &t2); + t1 &= ~(GI_MASK); + t1 |= gi; + t2 &= 0xFE; + t2 |= cpn ? 0x01 : 0x00; + lgs8gxx_write_reg(priv, 0x0C, t1); + lgs8gxx_write_reg(priv, 0x18, t2); + } else { + lgs8gxx_write_reg(priv, 0x04, gi); + } lgs8gxx_soft_reset(priv); - msleep(50); + err = lgs8gxx_wait_ca_lock(priv, locked); + if (err || !(*locked)) + return err; err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); if (err != 0) return err; if (ad_fini) { - err = lgs8gxx_is_locked(priv, locked); - if (err != 0) - return err; - } + dprintk("auto detect finished\n"); + } else + *locked = 0; return 0; } @@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, dprintk("%s\n", __func__); lgs8gxx_set_mode_auto(priv); - /* Guard Interval */ - lgs8gxx_write_reg(priv, 0x03, 00); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x67, 0xAA); + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + } else { + /* Guard Interval */ + lgs8gxx_write_reg(priv, 0x03, 00); + } for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { tmp_gi = GI_945; - err = lgs8gxx_autolock_gi(priv, GI_945, &locked); + err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); if (err) goto out; if (locked) @@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, } for (j = 0; j < 2; j++) { tmp_gi = GI_420; - err = lgs8gxx_autolock_gi(priv, GI_420, &locked); + err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); if (err) goto out; if (locked) goto locked; } tmp_gi = GI_595; - err = lgs8gxx_autolock_gi(priv, GI_595, &locked); + err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); if (err) goto out; if (locked) @@ -317,8 +493,13 @@ locked: if ((err == 0) && (locked == 1)) { u8 t; - lgs8gxx_read_reg(priv, 0xA2, &t); - *detected_param = t; + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0xA2, &t); + *detected_param = t; + } else { + lgs8gxx_read_reg(priv, 0x1F, &t); + *detected_param = t & 0x3F; + } if (tmp_gi == GI_945) dprintk("GI 945 locked\n"); @@ -345,18 +526,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) if (err != 0) { dprintk("lgs8gxx_auto_detect failed\n"); - } + } else + dprintk("detected param = 0x%02X\n", detected_param); /* Apply detected parameters */ if (priv->config->prod == LGS8GXX_PROD_LGS8913) { u8 inter_leave_len = detected_param & TIM_MASK ; - inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40; + /* Fix 8913 time interleaver detection bug */ + inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; detected_param |= inter_leave_len; } - lgs8gxx_write_reg(priv, 0x7D, detected_param); - if (priv->config->prod == LGS8GXX_PROD_LGS8913) - lgs8gxx_write_reg(priv, 0xC0, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t; + lgs8gxx_read_reg(priv, 0x19, &t); + t &= 0x81; + t |= detected_param << 1; + lgs8gxx_write_reg(priv, 0x19, t); + } else { + lgs8gxx_write_reg(priv, 0x7D, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC0, detected_param); + } /* lgs8gxx_soft_reset(priv); */ /* Enter manual mode */ @@ -378,9 +569,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, u8 serial, u8 clk_pol, u8 clk_gated) { int ret = 0; - u8 t; + u8 t, reg_addr; - ret = lgs8gxx_read_reg(priv, 0xC2, &t); + reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; + ret = lgs8gxx_read_reg(priv, reg_addr, &t); if (ret != 0) return ret; @@ -389,13 +581,29 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; - ret = lgs8gxx_write_reg(priv, 0xC2, t); + ret = lgs8gxx_write_reg(priv, reg_addr, t); if (ret != 0) return ret; return 0; } +/* A/D input peak-to-peak voltage range */ +static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, + u8 sel) +{ + u8 r26 = 0x73, r27 = 0x90; + + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) + return 0; + + r26 |= (sel & 0x01) << 7; + r27 |= (sel & 0x02) >> 1; + lgs8gxx_write_reg(priv, 0x26, r26); + lgs8gxx_write_reg(priv, 0x27, r27); + + return 0; +} /* LGS8913 demod frontend functions */ @@ -417,6 +625,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv) return 0; } +static int lgs8g75_init_data(struct lgs8gxx_state *priv) +{ + const u8 *p = lgs8g75_initdat; + int i; + + lgs8gxx_write_reg(priv, 0xC6, 0x40); + + lgs8gxx_write_reg(priv, 0x3D, 0x04); + lgs8gxx_write_reg(priv, 0x39, 0x00); + + lgs8gxx_write_reg(priv, 0x3A, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3B, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + + for (i = 0; i < sizeof(lgs8g75_initdat); i++) { + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); + lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); + lgs8gxx_write_reg(priv, 0x3C, *p); + p++; + } + + lgs8gxx_write_reg(priv, 0x38, 0x00); + + return 0; +} + static int lgs8gxx_init(struct dvb_frontend *fe) { struct lgs8gxx_state *priv = @@ -429,6 +665,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe) lgs8gxx_read_reg(priv, 0, &data); dprintk("reg 0 = 0x%02X\n", data); + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_set_adc_vpp(priv, config->adc_vpp); + /* Setup MPEG output format */ err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, config->ts_clk_pol, @@ -439,8 +678,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe) if (config->prod == LGS8GXX_PROD_LGS8913) lgs8913_init(priv); lgs8gxx_set_if_freq(priv, priv->config->if_freq); - if (config->prod != LGS8GXX_PROD_LGS8913) - lgs8gxx_set_ad_mode(priv); + lgs8gxx_set_ad_mode(priv); return 0; } @@ -489,9 +727,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe, static int lgs8gxx_get_fe(struct dvb_frontend *fe, struct dvb_frontend_parameters *fe_params) { - struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 t; - dprintk("%s\n", __func__); /* TODO: get real readings from device */ @@ -501,29 +736,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe, /* bandwidth */ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; - - lgs8gxx_read_reg(priv, 0x7D, &t); fe_params->u.ofdm.code_rate_HP = FEC_AUTO; fe_params->u.ofdm.code_rate_LP = FEC_AUTO; - /* constellation */ - switch (t & SC_MASK) { - case SC_QAM64: - fe_params->u.ofdm.constellation = QAM_64; - break; - case SC_QAM32: - fe_params->u.ofdm.constellation = QAM_32; - break; - case SC_QAM16: - fe_params->u.ofdm.constellation = QAM_16; - break; - case SC_QAM4: - case SC_QAM4NR: - fe_params->u.ofdm.constellation = QPSK; - break; - default: - fe_params->u.ofdm.constellation = QAM_64; - } + fe_params->u.ofdm.constellation = QAM_AUTO; /* transmission mode */ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; @@ -552,9 +768,19 @@ static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) { struct lgs8gxx_state *priv = fe->demodulator_priv; s8 ret; - u8 t; + u8 t, locked = 0; dprintk("%s\n", __func__); + *fe_status = 0; + + lgs8gxx_get_afc_phase(priv); + lgs8gxx_is_locked(priv, &locked); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + if (locked) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) @@ -658,12 +884,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) return 0; } +static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; + s16 v = 0; + + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0xB1, &t); + v |= t; + v <<= 8; + lgs8gxx_read_reg(priv, 0xB0, &t); + v |= t; + + *signal = v; + dprintk("%s: signal=0x%02X\n", __func__, *signal); + + return 0; +} + static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) { struct lgs8gxx_state *priv = fe->demodulator_priv; if (priv->config->prod == LGS8GXX_PROD_LGS8913) return lgs8913_read_signal_strength(priv, signal); + else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + return lgs8g75_read_signal_strength(priv, signal); else return lgs8gxx_read_signal_agc(priv, signal); } @@ -674,7 +921,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) u8 t; *snr = 0; - lgs8gxx_read_reg(priv, 0x95, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + lgs8gxx_read_reg(priv, 0x34, &t); + else + lgs8gxx_read_reg(priv, 0x95, &t); dprintk("AVG Noise=0x%02X\n", t); *snr = 256 - t; *snr <<= 8; @@ -690,31 +940,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return 0; } +static void packet_counter_start(struct lgs8gxx_state *priv) +{ + u8 orig, t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &orig); + orig &= 0xE7; + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x18; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x01); + lgs8gxx_write_reg(priv, 0xC6, 0x41); + lgs8gxx_write_reg(priv, 0xC6, 0x01); + } +} + +static void packet_counter_stop(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &t); + t &= 0xE7; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x81); + } +} + static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) { struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 r0, r1, r2, r3; - u32 total_cnt, err_cnt; + u8 reg_err, reg_total, t; + u32 total_cnt = 0, err_cnt = 0; + int i; dprintk("%s\n", __func__); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - lgs8gxx_write_reg(priv, 0xc6, 0x41); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - + packet_counter_start(priv); msleep(200); + packet_counter_stop(priv); + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg_total = 0x28; reg_err = 0x2C; + } else { + reg_total = 0xD0; reg_err = 0xD4; + } - lgs8gxx_write_reg(priv, 0xc6, 0x81); - lgs8gxx_read_reg(priv, 0xd0, &r0); - lgs8gxx_read_reg(priv, 0xd1, &r1); - lgs8gxx_read_reg(priv, 0xd2, &r2); - lgs8gxx_read_reg(priv, 0xd3, &r3); - total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); - lgs8gxx_read_reg(priv, 0xd4, &r0); - lgs8gxx_read_reg(priv, 0xd5, &r1); - lgs8gxx_read_reg(priv, 0xd6, &r2); - lgs8gxx_read_reg(priv, 0xd7, &r3); - err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); + for (i = 0; i < 4; i++) { + total_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_total+3-i, &t); + total_cnt |= t; + } + for (i = 0; i < 4; i++) { + err_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_err+3-i, &t); + err_cnt |= t; + } dprintk("error=%d total=%d\n", err_cnt, total_cnt); if (total_cnt == 0) @@ -801,6 +1088,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, sizeof(struct dvb_frontend_ops)); priv->frontend.demodulator_priv = priv; + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_init_data(priv); + return &priv->frontend; error_out: diff --git a/drivers/media/dvb/frontends/lgs8gxx.h b/drivers/media/dvb/frontends/lgs8gxx.h index 321d366a830..33c3c5e162f 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.h +++ b/drivers/media/dvb/frontends/lgs8gxx.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -34,6 +34,7 @@ #define LGS8GXX_PROD_LGS8G42 3 #define LGS8GXX_PROD_LGS8G52 4 #define LGS8GXX_PROD_LGS8G54 5 +#define LGS8GXX_PROD_LGS8G75 6 struct lgs8gxx_config { @@ -70,6 +71,10 @@ struct lgs8gxx_config { /*IF use Negative center frequency*/ u8 if_neg_center; + /*8G75 internal ADC input range selection*/ + /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ + u8 adc_vpp; + /* slave address and configuration of the tuner */ u8 tuner_address; }; diff --git a/drivers/media/dvb/frontends/lgs8gxx_priv.h b/drivers/media/dvb/frontends/lgs8gxx_priv.h index 9776d30686d..8ef376f1414 100644 --- a/drivers/media/dvb/frontends/lgs8gxx_priv.h +++ b/drivers/media/dvb/frontends/lgs8gxx_priv.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -38,7 +38,7 @@ struct lgs8gxx_state { #define SC_QAM64 0x10 /* 64QAM modulation */ #define SC_QAM32 0x0C /* 32QAM modulation */ #define SC_QAM16 0x08 /* 16QAM modulation */ -#define SC_QAM4NR 0x04 /* 4QAM modulation */ +#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ #define SC_QAM4 0x00 /* 4QAM modulation */ #define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ @@ -47,8 +47,8 @@ struct lgs8gxx_state { #define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ #define TIM_MASK 0x20 /* Time Interleave Length Mask */ -#define TIM_LONG 0x00 /* Time Interleave Length = 720 */ -#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */ +#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ +#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ #define CF_MASK 0x80 /* Control Frame Mask */ #define CF_EN 0x80 /* Control Frame On */ diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c index a621f727935..472907d4346 100644 --- a/drivers/media/dvb/frontends/mt312.c +++ b/drivers/media/dvb/frontends/mt312.c @@ -85,7 +85,7 @@ static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, int i; dprintk("R(%d):", reg & 0x7f); for (i = 0; i < count; i++) - printk(" %02x", buf[i]); + printk(KERN_CONT " %02x", buf[i]); printk("\n"); } @@ -103,7 +103,7 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, int i; dprintk("W(%d):", reg & 0x7f); for (i = 0; i < count; i++) - printk(" %02x", src[i]); + printk(KERN_CONT " %02x", src[i]); printk("\n"); } @@ -744,7 +744,8 @@ static struct dvb_frontend_ops mt312_ops = { .type = FE_QPSK, .frequency_min = 950000, .frequency_max = 2150000, - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, /* FIXME: adjust freq to real used xtal */ + /* FIXME: adjust freq to real used xtal */ + .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ .symbol_rate_max = MT312_SYS_CLK / 2, .caps = @@ -782,7 +783,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct mt312_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c index 0eef22dbf8a..a763ec756f7 100644 --- a/drivers/media/dvb/frontends/nxt6000.c +++ b/drivers/media/dvb/frontends/nxt6000.c @@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct nxt6000_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index 8133ea3cddd..38e67accb8c 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct or51132_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c index 16cf2fdd5d7..c709ce6771c 100644 --- a/drivers/media/dvb/frontends/or51211.c +++ b/drivers/media/dvb/frontends/or51211.c @@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct or51211_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index 3e08d985d6e..fb301151842 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c @@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c index 66e2dd6d6fe..d8adf1e3201 100644 --- a/drivers/media/dvb/frontends/s5h1411.c +++ b/drivers/media/dvb/frontends/s5h1411.c @@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c index 3f5a0e1dfdf..3156b64cfc9 100644 --- a/drivers/media/dvb/frontends/s921_module.c +++ b/drivers/media/dvb/frontends/s921_module.c @@ -169,6 +169,8 @@ struct dvb_frontend* s921_attach(const struct s921_config *config, struct s921_state *state; state = kzalloc(sizeof(struct s921_state), GFP_KERNEL); + if (state == NULL) + return NULL; state->addr = config->i2c_address; state->i2c = i2c; diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c index 0bd16af8a6c..9552a22ccff 100644 --- a/drivers/media/dvb/frontends/si21xx.c +++ b/drivers/media/dvb/frontends/si21xx.c @@ -928,7 +928,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, dprintk("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL); + state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c index 1c9a9b4051b..b85eb60a893 100644 --- a/drivers/media/dvb/frontends/sp8870.c +++ b/drivers/media/dvb/frontends/sp8870.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct sp8870_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index 559509ab4da..4a7c3d84260 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct sp887x_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c index 1ed5a7db4c5..60ee18a94f4 100644 --- a/drivers/media/dvb/frontends/stb6100.c +++ b/drivers/media/dvb/frontends/stb6100.c @@ -367,7 +367,9 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */ nint = fvco / (state->reference << psd2); /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */ - nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference; + nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2)) + << (9 - psd2), + state->reference); dprintk(verbose, FE_DEBUG, 1, "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u", frequency, srate, (unsigned int)g, (unsigned int)odiv, diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c index ff1194de34c..2930a5d6768 100644 --- a/drivers/media/dvb/frontends/stv0288.c +++ b/drivers/media/dvb/frontends/stv0288.c @@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c index 62caf802ed9..4fd7479bb62 100644 --- a/drivers/media/dvb/frontends/stv0297.c +++ b/drivers/media/dvb/frontends/stv0297.c @@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, struct stv0297_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 6c1cb1973c6..96887446972 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 1da045fbb4e..3bde3324a03 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -230,8 +230,8 @@ enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params) stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c); stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c); stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f); - stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x24); - stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x24); + stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20); + stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20); stv0900_write_reg(i_params, R0900_NCOARSE, 0x13); msleep(3); stv0900_write_reg(i_params, R0900_I2CCFG, 0x08); @@ -370,8 +370,8 @@ static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) u32 fi2c; dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON); - if (enable) - stv0900_write_bits(i_params, fi2c, 1); + + stv0900_write_bits(i_params, fi2c, enable); return 0; } diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index a5a31536cbc..962fde1437c 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -1721,7 +1721,7 @@ static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_front s32 srate, demod_timeout, fec_timeout, freq1, freq0; - enum fe_stv0900_signal_type signal_type = STV0900_NODATA;; + enum fe_stv0900_signal_type signal_type = STV0900_NODATA; switch (demod) { case STV0900_DEMOD_1: diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c index 70efac869d2..dcf1b21ea97 100644 --- a/drivers/media/dvb/frontends/stv6110.c +++ b/drivers/media/dvb/frontends/stv6110.c @@ -36,6 +36,7 @@ struct stv6110_priv { struct i2c_adapter *i2c; u32 mclk; + u8 clk_div; u8 regs[8]; }; @@ -100,35 +101,25 @@ static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], struct stv6110_priv *priv = fe->tuner_priv; int rc; u8 reg[] = { start }; - struct i2c_msg msg_wr = { - .addr = priv->i2c_address, - .flags = 0, - .buf = reg, - .len = 1, + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = reg, + .len = 1, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .buf = regs, + .len = len, + }, }; - struct i2c_msg msg_rd = { - .addr = priv->i2c_address, - .flags = I2C_M_RD, - .buf = regs, - .len = len, - }; - /* write subaddr */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - rc = i2c_transfer(priv->i2c, &msg_wr, 1); - if (rc != 1) - dprintk("%s: i2c error\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - /* read registers */ - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - rc = i2c_transfer(priv->i2c, &msg_rd, 1); - if (rc != 1) + rc = i2c_transfer(priv->i2c, msg, 2); + if (rc != 2) dprintk("%s: i2c error\n", __func__); if (fe->ops.i2c_gate_ctrl) @@ -221,6 +212,10 @@ static int stv6110_init(struct dvb_frontend *fe) priv->regs[RSTV6110_CTRL1] |= ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); + /* divisor value for the output clock */ + priv->regs[RSTV6110_CTRL2] &= ~0xc0; + priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); msleep(1); stv6110_set_bandwidth(fe, 72000000); @@ -418,6 +413,10 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, }; int ret; + /* divisor value for the output clock */ + reg0[2] &= ~0xc0; + reg0[2] |= (config->clk_div << 6); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -436,6 +435,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, priv->i2c_address = config->i2c_address; priv->i2c = i2c; priv->mclk = config->mclk; + priv->clk_div = config->clk_div; memcpy(&priv->regs, ®0[1], 8); diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h index 1c0314d6aa5..9db2402410f 100644 --- a/drivers/media/dvb/frontends/stv6110.h +++ b/drivers/media/dvb/frontends/stv6110.h @@ -41,7 +41,7 @@ struct stv6110_config { u8 i2c_address; u32 mclk; - int iq_wiring; + u8 clk_div; /* divisor value for the output clock */ }; #if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \ diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index f648fdb64bb..6c1dbf9288d 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -176,7 +176,7 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate tmp = ((symbolrate << 4) % FIN) << 8; ratio = (ratio << 8) + tmp / FIN; tmp = (tmp % FIN) << 8; - ratio = (ratio << 8) + (tmp + FIN/2) / FIN; + ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN); BDR = ratio; BDRI = (((XIN << 5) / symbolrate) + 1) / 2; @@ -413,7 +413,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, u8 id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c index cc8862ce4aa..4e2a7c8b2f6 100644 --- a/drivers/media/dvb/frontends/tda10048.c +++ b/drivers/media/dvb/frontends/tda10048.c @@ -1095,7 +1095,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, dprintk(1, "%s()\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 4981cef8b44..f2a8abe0a24 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; @@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c index a17ce3c4ad8..f2c8faac6f3 100644 --- a/drivers/media/dvb/frontends/tda10086.c +++ b/drivers/media/dvb/frontends/tda10086.c @@ -745,7 +745,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, dprintk ("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); if (!state) return NULL; diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c index 5b843b2e67e..9369f7442f2 100644 --- a/drivers/media/dvb/frontends/tda8083.c +++ b/drivers/media/dvb/frontends/tda8083.c @@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct tda8083_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c index b6d17779910..320c3c36d8b 100644 --- a/drivers/media/dvb/frontends/tda8261.c +++ b/drivers/media/dvb/frontends/tda8261.c @@ -136,9 +136,9 @@ static int tda8261_set_state(struct dvb_frontend *fe, if (frequency < 1450000) buf[3] = 0x00; - if (frequency < 2000000) + else if (frequency < 2000000) buf[3] = 0x40; - if (frequency < 2150000) + else if (frequency < 2150000) buf[3] = 0x80; /* Set params */ diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c index a184597f1d9..550a07a8a99 100644 --- a/drivers/media/dvb/frontends/ves1820.c +++ b/drivers/media/dvb/frontends/ves1820.c @@ -165,7 +165,7 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) tmp = ((symbolrate << 4) % fin) << 8; ratio = (ratio << 8) + tmp / fin; tmp = (tmp % fin) << 8; - ratio = (ratio << 8) + (tmp + fin / 2) / fin; + ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin); BDR = ratio; BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; @@ -374,7 +374,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct ves1820_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c index bd558960bd8..8d7854c2fb0 100644 --- a/drivers/media/dvb/frontends/ves1x93.c +++ b/drivers/media/dvb/frontends/ves1x93.c @@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, u8 identity; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c index e22a0b381dc..4e814ff22b2 100644 --- a/drivers/media/dvb/frontends/zl10036.c +++ b/drivers/media/dvb/frontends/zl10036.c @@ -29,7 +29,7 @@ #include <linux/module.h> #include <linux/dvb/frontend.h> -#include <asm/types.h> +#include <linux/types.h> #include "zl10036.h" diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb/frontends/zl10039.c new file mode 100644 index 00000000000..11b29cb883e --- /dev/null +++ b/drivers/media/dvb/frontends/zl10039.c @@ -0,0 +1,308 @@ +/* + * Driver for Zarlink ZL10039 DVB-S tuner + * + * Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za> + * + * 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/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/dvb/frontend.h> + +#include "dvb_frontend.h" +#include "zl10039.h" + +static int debug; + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG args); \ + } while (0) + +enum zl10039_model_id { + ID_ZL10039 = 1 +}; + +struct zl10039_state { + struct i2c_adapter *i2c; + u8 i2c_addr; + u8 id; +}; + +enum zl10039_reg_addr { + PLL0 = 0, + PLL1, + PLL2, + PLL3, + RFFE, + BASE0, + BASE1, + BASE2, + LO0, + LO1, + LO2, + LO3, + LO4, + LO5, + LO6, + GENERAL +}; + +static int zl10039_read(const struct zl10039_state *state, + const enum zl10039_reg_addr reg, u8 *buf, + const size_t count) +{ + u8 regbuf[] = { reg }; + struct i2c_msg msg[] = { + {/* Write register address */ + .addr = state->i2c_addr, + .flags = 0, + .buf = regbuf, + .len = 1, + }, {/* Read count bytes */ + .addr = state->i2c_addr, + .flags = I2C_M_RD, + .buf = buf, + .len = count, + }, + }; + + dprintk("%s\n", __func__); + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + dprintk("%s: i2c read error\n", __func__); + return -EREMOTEIO; + } + + return 0; /* Success */ +} + +static int zl10039_write(struct zl10039_state *state, + const enum zl10039_reg_addr reg, const u8 *src, + const size_t count) +{ + u8 buf[count + 1]; + struct i2c_msg msg = { + .addr = state->i2c_addr, + .flags = 0, + .buf = buf, + .len = count + 1, + }; + + dprintk("%s\n", __func__); + /* Write register address and data in one go */ + buf[0] = reg; + memcpy(&buf[1], src, count); + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + dprintk("%s: i2c write error\n", __func__); + return -EREMOTEIO; + } + + return 0; /* Success */ +} + +static inline int zl10039_readreg(struct zl10039_state *state, + const enum zl10039_reg_addr reg, u8 *val) +{ + return zl10039_read(state, reg, val, 1); +} + +static inline int zl10039_writereg(struct zl10039_state *state, + const enum zl10039_reg_addr reg, + const u8 val) +{ + return zl10039_write(state, reg, &val, 1); +} + +static int zl10039_init(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + int ret; + + dprintk("%s\n", __func__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* Reset logic */ + ret = zl10039_writereg(state, GENERAL, 0x40); + if (ret < 0) { + dprintk("Note: i2c write error normal when resetting the " + "tuner\n"); + } + /* Wake up */ + ret = zl10039_writereg(state, GENERAL, 0x01); + if (ret < 0) { + dprintk("Tuner power up failed\n"); + return ret; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int zl10039_sleep(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + int ret; + + dprintk("%s\n", __func__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = zl10039_writereg(state, GENERAL, 0x80); + if (ret < 0) { + dprintk("Tuner sleep failed\n"); + return ret; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int zl10039_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct zl10039_state *state = fe->tuner_priv; + u8 buf[6]; + u8 bf; + u32 fbw; + u32 div; + int ret; + + dprintk("%s\n", __func__); + dprintk("Set frequency = %d, symbol rate = %d\n", + params->frequency, params->u.qpsk.symbol_rate); + + /* Assumed 10.111 MHz crystal oscillator */ + /* Cancelled num/den 80 to prevent overflow */ + div = (params->frequency * 1000) / 126387; + fbw = (params->u.qpsk.symbol_rate * 27) / 32000; + /* Cancelled num/den 10 to prevent overflow */ + bf = ((fbw * 5088) / 1011100) - 1; + + /*PLL divider*/ + buf[0] = (div >> 8) & 0x7f; + buf[1] = (div >> 0) & 0xff; + /*Reference divider*/ + /* Select reference ratio of 80 */ + buf[2] = 0x1D; + /*PLL test modes*/ + buf[3] = 0x40; + /*RF Control register*/ + buf[4] = 0x6E; /* Bypass enable */ + /*Baseband filter cutoff */ + buf[5] = bf; + + /* Open i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* BR = 10, Enable filter adjustment */ + ret = zl10039_writereg(state, BASE1, 0x0A); + if (ret < 0) + goto error; + /* Write new config values */ + ret = zl10039_write(state, PLL0, buf, sizeof(buf)); + if (ret < 0) + goto error; + /* BR = 10, Disable filter adjustment */ + ret = zl10039_writereg(state, BASE1, 0x6A); + if (ret < 0) + goto error; + + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return 0; +error: + dprintk("Error setting tuner\n"); + return ret; +} + +static int zl10039_release(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + + dprintk("%s\n", __func__); + kfree(state); + fe->tuner_priv = NULL; + return 0; +} + +static struct dvb_tuner_ops zl10039_ops = { + .release = zl10039_release, + .init = zl10039_init, + .sleep = zl10039_sleep, + .set_params = zl10039_set_params, +}; + +struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, struct i2c_adapter *i2c) +{ + struct zl10039_state *state = NULL; + + dprintk("%s\n", __func__); + state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->i2c = i2c; + state->i2c_addr = i2c_addr; + + /* Open i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* check if this is a valid tuner */ + if (zl10039_readreg(state, GENERAL, &state->id) < 0) { + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + goto error; + } + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + state->id = state->id & 0x0f; + switch (state->id) { + case ID_ZL10039: + strcpy(fe->ops.tuner_ops.info.name, + "Zarlink ZL10039 DVB-S tuner"); + break; + default: + dprintk("Chip ID=%x does not match a known type\n", state->id); + break; + goto error; + } + + memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = state; + dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr); + return fe; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(zl10039_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver"); +MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/zl10039.h b/drivers/media/dvb/frontends/zl10039.h new file mode 100644 index 00000000000..5eee7ea162a --- /dev/null +++ b/drivers/media/dvb/frontends/zl10039.h @@ -0,0 +1,40 @@ +/* + Driver for Zarlink ZL10039 DVB-S tuner + + Copyright (C) 2007 Jan D. Louw <jd.louw@mweb.co.za> + + 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 ZL10039_H +#define ZL10039_H + +#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \ + && defined(MODULE)) +struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ZL10039 */ + +#endif /* ZL10039_H */ diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 148b6f7f6cb..8c612719adf 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -38,6 +38,8 @@ struct zl10353_state { struct zl10353_config config; enum fe_bandwidth bandwidth; + u32 ucblocks; + u32 frequency; }; static int debug; @@ -98,7 +100,6 @@ static int zl10353_read_register(struct zl10353_state *state, u8 reg) static void zl10353_dump_regs(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; - char buf[52], buf2[4]; int ret; u8 reg; @@ -106,19 +107,18 @@ static void zl10353_dump_regs(struct dvb_frontend *fe) for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk(KERN_DEBUG "%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } ret = zl10353_read_register(state, reg); if (ret >= 0) - sprintf(buf2, "%02x ", (u8)ret); + printk(KERN_CONT " %02x", (u8)ret); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0xff) break; } - printk(KERN_DEBUG "%s\n", buf); + printk(KERN_CONT "\n"); } static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, @@ -201,6 +201,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, u16 tps = 0; struct dvb_ofdm_parameters *op = ¶m->u.ofdm; + state->frequency = param->frequency; + zl10353_single_write(fe, RESET, 0x80); udelay(200); zl10353_single_write(fe, 0xEA, 0x01); @@ -466,7 +468,7 @@ static int zl10353_get_parameters(struct dvb_frontend *fe, break; } - param->frequency = 0; + param->frequency = state->frequency; op->bandwidth = state->bandwidth; param->inversion = INVERSION_AUTO; @@ -544,9 +546,13 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct zl10353_state *state = fe->demodulator_priv; + u32 ubl = 0; + + ubl = zl10353_read_register(state, RS_UBC_1) << 8 | + zl10353_read_register(state, RS_UBC_0); - *ucblocks = zl10353_read_register(state, RS_UBC_1) << 8 | - zl10353_read_register(state, RS_UBC_0); + state->ucblocks += ubl; + *ucblocks = state->ucblocks; return 0; } diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 598eaf8acc6..80d14a065ba 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -439,7 +439,7 @@ static inline u32 divide(u32 numerator, u32 denominator) if (denominator == 0) return ~0; - return (numerator + denominator / 2) / denominator; + return DIV_ROUND_CLOSEST(numerator, denominator); } /* LG Innotek TDTE-E001P (Infineon TUA6034) */ diff --git a/drivers/media/dvb/pt1/Kconfig b/drivers/media/dvb/pt1/Kconfig new file mode 100644 index 00000000000..24501d5bf70 --- /dev/null +++ b/drivers/media/dvb/pt1/Kconfig @@ -0,0 +1,12 @@ +config DVB_PT1 + tristate "PT1 cards" + depends on DVB_CORE && PCI && I2C + help + Support for Earthsoft PT1 PCI cards. + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y or M if you own such a device and want to use it. + diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile new file mode 100644 index 00000000000..a66da17bbe3 --- /dev/null +++ b/drivers/media/dvb/pt1/Makefile @@ -0,0 +1,5 @@ +earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o + +obj-$(CONFIG_DVB_PT1) += earth-pt1.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c new file mode 100644 index 00000000000..81e623a90f0 --- /dev/null +++ b/drivers/media/dvb/pt1/pt1.c @@ -0,0 +1,1057 @@ +/* + * driver for Earthsoft PT1 + * + * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/pci.h> +#include <linux/kthread.h> +#include <linux/freezer.h> + +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#include "va1j5jf8007t.h" +#include "va1j5jf8007s.h" + +#define DRIVER_NAME "earth-pt1" + +#define PT1_PAGE_SHIFT 12 +#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) +#define PT1_NR_UPACKETS 1024 +#define PT1_NR_BUFS 511 + +struct pt1_buffer_page { + __le32 upackets[PT1_NR_UPACKETS]; +}; + +struct pt1_table_page { + __le32 next_pfn; + __le32 buf_pfns[PT1_NR_BUFS]; +}; + +struct pt1_buffer { + struct pt1_buffer_page *page; + dma_addr_t addr; +}; + +struct pt1_table { + struct pt1_table_page *page; + dma_addr_t addr; + struct pt1_buffer bufs[PT1_NR_BUFS]; +}; + +#define PT1_NR_ADAPS 4 + +struct pt1_adapter; + +struct pt1 { + struct pci_dev *pdev; + void __iomem *regs; + struct i2c_adapter i2c_adap; + int i2c_running; + struct pt1_adapter *adaps[PT1_NR_ADAPS]; + struct pt1_table *tables; + struct task_struct *kthread; +}; + +struct pt1_adapter { + struct pt1 *pt1; + int index; + + u8 *buf; + int upacket_count; + int packet_count; + + struct dvb_adapter adap; + struct dvb_demux demux; + int users; + struct dmxdev dmxdev; + struct dvb_net net; + struct dvb_frontend *fe; + int (*orig_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); +}; + +#define pt1_printk(level, pt1, format, arg...) \ + dev_printk(level, &(pt1)->pdev->dev, format, ##arg) + +static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) +{ + writel(data, pt1->regs + reg * 4); +} + +static u32 pt1_read_reg(struct pt1 *pt1, int reg) +{ + return readl(pt1->regs + reg * 4); +} + +static int pt1_nr_tables = 64; +module_param_named(nr_tables, pt1_nr_tables, int, 0); + +static void pt1_increment_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000020); +} + +static void pt1_init_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000010); +} + +static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) +{ + pt1_write_reg(pt1, 5, first_pfn); + pt1_write_reg(pt1, 0, 0x0c000040); +} + +static void pt1_unregister_tables(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x08080000); +} + +static int pt1_sync(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 57; i++) { + if (pt1_read_reg(pt1, 0) & 0x20000000) + return 0; + pt1_write_reg(pt1, 0, 0x00000008); + } + pt1_printk(KERN_ERR, pt1, "could not sync\n"); + return -EIO; +} + +static u64 pt1_identify(struct pt1 *pt1) +{ + int i; + u64 id; + id = 0; + for (i = 0; i < 57; i++) { + id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; + pt1_write_reg(pt1, 0, 0x00000008); + } + return id; +} + +static int pt1_unlock(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x00000008); + for (i = 0; i < 3; i++) { + if (pt1_read_reg(pt1, 0) & 0x80000000) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not unlock\n"); + return -EIO; +} + +static int pt1_reset_pci(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x01010000); + pt1_write_reg(pt1, 0, 0x01000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000001) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); + return -EIO; +} + +static int pt1_reset_ram(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x02020000); + pt1_write_reg(pt1, 0, 0x02000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000002) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); + return -EIO; +} + +static int pt1_do_enable_ram(struct pt1 *pt1) +{ + int i, j; + u32 status; + status = pt1_read_reg(pt1, 0) & 0x00000004; + pt1_write_reg(pt1, 0, 0x00000002); + for (i = 0; i < 10; i++) { + for (j = 0; j < 1024; j++) { + if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) + return 0; + } + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); + return -EIO; +} + +static int pt1_enable_ram(struct pt1 *pt1) +{ + int i, ret; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + for (i = 0; i < 10; i++) { + ret = pt1_do_enable_ram(pt1); + if (ret < 0) + return ret; + } + return 0; +} + +static void pt1_disable_ram(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x0b0b0000); +} + +static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) +{ + pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); +} + +static void pt1_init_streams(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_set_stream(pt1, i, 0); +} + +static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) +{ + u32 upacket; + int i; + int index; + struct pt1_adapter *adap; + int offset; + u8 *buf; + + if (!page->upackets[PT1_NR_UPACKETS - 1]) + return 0; + + for (i = 0; i < PT1_NR_UPACKETS; i++) { + upacket = le32_to_cpu(page->upackets[i]); + index = (upacket >> 29) - 1; + if (index < 0 || index >= PT1_NR_ADAPS) + continue; + + adap = pt1->adaps[index]; + if (upacket >> 25 & 1) + adap->upacket_count = 0; + else if (!adap->upacket_count) + continue; + + buf = adap->buf; + offset = adap->packet_count * 188 + adap->upacket_count * 3; + buf[offset] = upacket >> 16; + buf[offset + 1] = upacket >> 8; + if (adap->upacket_count != 62) + buf[offset + 2] = upacket; + + if (++adap->upacket_count >= 63) { + adap->upacket_count = 0; + if (++adap->packet_count >= 21) { + dvb_dmx_swfilter_packets(&adap->demux, buf, 21); + adap->packet_count = 0; + } + } + } + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + return 1; +} + +static int pt1_thread(void *data) +{ + struct pt1 *pt1; + int table_index; + int buf_index; + struct pt1_buffer_page *page; + + pt1 = data; + set_freezable(); + + table_index = 0; + buf_index = 0; + + while (!kthread_should_stop()) { + try_to_freeze(); + + page = pt1->tables[table_index].bufs[buf_index].page; + if (!pt1_filter(pt1, page)) { + schedule_timeout_interruptible((HZ + 999) / 1000); + continue; + } + + if (++buf_index >= PT1_NR_BUFS) { + pt1_increment_table_count(pt1); + buf_index = 0; + if (++table_index >= pt1_nr_tables) + table_index = 0; + } + } + + return 0; +} + +static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) +{ + dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); +} + +static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) +{ + void *page; + dma_addr_t addr; + + page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, + GFP_KERNEL); + if (page == NULL) + return NULL; + + BUG_ON(addr & (PT1_PAGE_SIZE - 1)); + BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); + + *addrp = addr; + *pfnp = addr >> PT1_PAGE_SHIFT; + return page; +} + +static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) +{ + pt1_free_page(pt1, buf->page, buf->addr); +} + +static int +pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) +{ + struct pt1_buffer_page *page; + dma_addr_t addr; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + + buf->page = page; + buf->addr = addr; + return 0; +} + +static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) +{ + int i; + + for (i = 0; i < PT1_NR_BUFS; i++) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, table->page, table->addr); +} + +static int +pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) +{ + struct pt1_table_page *page; + dma_addr_t addr; + int i, ret; + u32 buf_pfn; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + for (i = 0; i < PT1_NR_BUFS; i++) { + ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); + if (ret < 0) + goto err; + + page->buf_pfns[i] = cpu_to_le32(buf_pfn); + } + + pt1_increment_table_count(pt1); + table->page = page; + table->addr = addr; + return 0; + +err: + while (i--) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, page, addr); + return ret; +} + +static void pt1_cleanup_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i; + + tables = pt1->tables; + pt1_unregister_tables(pt1); + + for (i = 0; i < pt1_nr_tables; i++) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); +} + +static int pt1_init_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i, ret; + u32 first_pfn, pfn; + + tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); + if (tables == NULL) + return -ENOMEM; + + pt1_init_table_count(pt1); + + i = 0; + if (pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[0], &first_pfn); + if (ret) + goto err; + i++; + } + + while (i < pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[i], &pfn); + if (ret) + goto err; + tables[i - 1].page->next_pfn = cpu_to_le32(pfn); + i++; + } + + tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); + + pt1_register_tables(pt1, first_pfn); + pt1->tables = tables; + return 0; + +err: + while (i--) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); + return ret; +} + +static int pt1_start_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!adap->users++) + pt1_set_stream(adap->pt1, adap->index, 1); + return 0; +} + +static int pt1_stop_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!--adap->users) + pt1_set_stream(adap->pt1, adap->index, 0); + return 0; +} + +static void +pt1_set_power(struct pt1 *pt1, int power, int lnb, int reset) +{ + pt1_write_reg(pt1, 1, power | lnb << 1 | !reset << 3); +} + +static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct pt1_adapter *adap; + int lnb; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + + switch (voltage) { + case SEC_VOLTAGE_13: /* actually 11V */ + lnb = 2; + break; + case SEC_VOLTAGE_18: /* actually 15V */ + lnb = 3; + break; + case SEC_VOLTAGE_OFF: + lnb = 0; + break; + default: + return -EINVAL; + } + + pt1_set_power(adap->pt1, 1, lnb, 0); + + if (adap->orig_set_voltage) + return adap->orig_set_voltage(fe, voltage); + else + return 0; +} + +static void pt1_free_adapter(struct pt1_adapter *adap) +{ + dvb_unregister_frontend(adap->fe); + dvb_net_release(&adap->net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->adap); + free_page((unsigned long)adap->buf); + kfree(adap); +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct pt1_adapter * +pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + void *buf; + struct dvb_adapter *dvb_adap; + struct dvb_demux *demux; + struct dmxdev *dmxdev; + int ret; + + adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); + if (!adap) { + ret = -ENOMEM; + goto err; + } + + adap->pt1 = pt1; + + adap->orig_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = pt1_set_voltage; + + buf = (u8 *)__get_free_page(GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_kfree; + } + + adap->buf = buf; + adap->upacket_count = 0; + adap->packet_count = 0; + + dvb_adap = &adap->adap; + dvb_adap->priv = adap; + ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, + &pt1->pdev->dev, adapter_nr); + if (ret < 0) + goto err_free_page; + + demux = &adap->demux; + demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + demux->priv = adap; + demux->feednum = 256; + demux->filternum = 256; + demux->start_feed = pt1_start_feed; + demux->stop_feed = pt1_stop_feed; + demux->write_to_decoder = NULL; + ret = dvb_dmx_init(demux); + if (ret < 0) + goto err_unregister_adapter; + + dmxdev = &adap->dmxdev; + dmxdev->filternum = 256; + dmxdev->demux = &demux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adap); + if (ret < 0) + goto err_dmx_release; + + dvb_net_init(dvb_adap, &adap->net, &demux->dmx); + + ret = dvb_register_frontend(dvb_adap, fe); + if (ret < 0) + goto err_net_release; + adap->fe = fe; + + return adap; + +err_net_release: + dvb_net_release(&adap->net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); +err_dmx_release: + dvb_dmx_release(demux); +err_unregister_adapter: + dvb_unregister_adapter(dvb_adap); +err_free_page: + free_page((unsigned long)buf); +err_kfree: + kfree(adap); +err: + return ERR_PTR(ret); +} + +static void pt1_cleanup_adapters(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_free_adapter(pt1->adaps[i]); +} + +struct pt1_config { + struct va1j5jf8007s_config va1j5jf8007s_config; + struct va1j5jf8007t_config va1j5jf8007t_config; +}; + +static const struct pt1_config pt1_configs[2] = { + { + { .demod_address = 0x1b }, + { .demod_address = 0x1a }, + }, { + { .demod_address = 0x19 }, + { .demod_address = 0x18 }, + }, +}; + +static int pt1_init_adapters(struct pt1 *pt1) +{ + int i, j; + struct i2c_adapter *i2c_adap; + const struct pt1_config *config; + struct dvb_frontend *fe[4]; + struct pt1_adapter *adap; + int ret; + + i = 0; + j = 0; + + i2c_adap = &pt1->i2c_adap; + do { + config = &pt1_configs[i / 2]; + + fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; /* This does not sound nice... */ + goto err; + } + i++; + + fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; + goto err; + } + i++; + + ret = va1j5jf8007s_prepare(fe[i - 2]); + if (ret < 0) + goto err; + + ret = va1j5jf8007t_prepare(fe[i - 1]); + if (ret < 0) + goto err; + + } while (i < 4); + + do { + adap = pt1_alloc_adapter(pt1, fe[j]); + if (IS_ERR(adap)) + goto err; + adap->index = j; + pt1->adaps[j] = adap; + } while (++j < 4); + + return 0; + +err: + while (i-- > j) + fe[i]->ops.release(fe[i]); + + while (j--) + pt1_free_adapter(pt1->adaps[j]); + + return ret; +} + +static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, + int clock, int data, int next_addr) +{ + pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | + !clock << 11 | !data << 10 | next_addr); +} + +static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); + *addrp = addr + 3; +} + +static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); + pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); + *addrp = addr + 4; +} + +static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); + pt1_i2c_write_bit(pt1, addr, &addr, 1); + *addrp = addr; +} + +static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_read_bit(pt1, addr, &addr); + pt1_i2c_write_bit(pt1, addr, &addr, last); + *addrp = addr; +} + +static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); + *addrp = addr + 3; +} + +static void +pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); + *addrp = addr; +} + +static void +pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); + *addrp = addr; +} + +static int pt1_i2c_end(struct pt1 *pt1, int addr) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); + + pt1_write_reg(pt1, 0, 0x00000004); + do { + if (signal_pending(current)) + return -EINTR; + schedule_timeout_interruptible((HZ + 999) / 1000); + } while (pt1_read_reg(pt1, 0) & 0x00000080); + return 0; +} + +static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) +{ + int addr; + addr = 0; + + pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); + addr = addr + 1; + + if (!pt1->i2c_running) { + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + addr = addr + 2; + pt1->i2c_running = 1; + } + *addrp = addr; +} + +static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct pt1 *pt1; + int i; + struct i2c_msg *msg, *next_msg; + int addr, ret; + u16 len; + u32 word; + + pt1 = i2c_get_adapdata(adap); + + for (i = 0; i < num; i++) { + msg = &msgs[i]; + if (msg->flags & I2C_M_RD) + return -ENOTSUPP; + + if (i + 1 < num) + next_msg = &msgs[i + 1]; + else + next_msg = NULL; + + if (next_msg && next_msg->flags & I2C_M_RD) { + i++; + + len = next_msg->len; + if (len > 4) + return -ENOTSUPP; + + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + pt1_i2c_read_msg(pt1, addr, &addr, next_msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + + word = pt1_read_reg(pt1, 2); + while (len--) { + next_msg->buf[len] = word; + word >>= 8; + } + } else { + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + } + } + + return num; +} + +static u32 pt1_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm pt1_i2c_algo = { + .master_xfer = pt1_i2c_xfer, + .functionality = pt1_i2c_func, +}; + +static void pt1_i2c_wait(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 128; i++) + pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); +} + +static void pt1_i2c_init(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 1024; i++) + pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); +} + +static void __devexit pt1_remove(struct pci_dev *pdev) +{ + struct pt1 *pt1; + void __iomem *regs; + + pt1 = pci_get_drvdata(pdev); + regs = pt1->regs; + + kthread_stop(pt1->kthread); + pt1_cleanup_tables(pt1); + pt1_cleanup_adapters(pt1); + pt1_disable_ram(pt1); + pt1_set_power(pt1, 0, 0, 1); + i2c_del_adapter(&pt1->i2c_adap); + pci_set_drvdata(pdev, NULL); + kfree(pt1); + pci_iounmap(pdev, regs); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int __devinit +pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + void __iomem *regs; + struct pt1 *pt1; + struct i2c_adapter *i2c_adap; + struct task_struct *kthread; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err; + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_disable_device; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret < 0) + goto err_pci_disable_device; + + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + ret = -EIO; + goto err_pci_release_regions; + } + + pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); + if (!pt1) { + ret = -ENOMEM; + goto err_pci_iounmap; + } + + pt1->pdev = pdev; + pt1->regs = regs; + pci_set_drvdata(pdev, pt1); + + i2c_adap = &pt1->i2c_adap; + i2c_adap->class = I2C_CLASS_TV_DIGITAL; + i2c_adap->algo = &pt1_i2c_algo; + i2c_adap->algo_data = NULL; + i2c_adap->dev.parent = &pdev->dev; + i2c_set_adapdata(i2c_adap, pt1); + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) + goto err_kfree; + + pt1_set_power(pt1, 0, 0, 1); + + pt1_i2c_init(pt1); + pt1_i2c_wait(pt1); + + ret = pt1_sync(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_identify(pt1); + + ret = pt1_unlock(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_pci(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_enable_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_init_streams(pt1); + + pt1_set_power(pt1, 1, 0, 1); + schedule_timeout_uninterruptible((HZ + 49) / 50); + + pt1_set_power(pt1, 1, 0, 0); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + ret = pt1_init_adapters(pt1); + if (ret < 0) + goto err_pt1_disable_ram; + + ret = pt1_init_tables(pt1); + if (ret < 0) + goto err_pt1_cleanup_adapters; + + kthread = kthread_run(pt1_thread, pt1, "pt1"); + if (IS_ERR(kthread)) { + ret = PTR_ERR(kthread); + goto err_pt1_cleanup_tables; + } + + pt1->kthread = kthread; + return 0; + +err_pt1_cleanup_tables: + pt1_cleanup_tables(pt1); +err_pt1_cleanup_adapters: + pt1_cleanup_adapters(pt1); +err_pt1_disable_ram: + pt1_disable_ram(pt1); + pt1_set_power(pt1, 0, 0, 1); +err_i2c_del_adapter: + i2c_del_adapter(i2c_adap); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(pt1); +err_pci_iounmap: + pci_iounmap(pdev, regs); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err: + return ret; + +} + +static struct pci_device_id pt1_id_table[] = { + { PCI_DEVICE(0x10ee, 0x211a) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pt1_id_table); + +static struct pci_driver pt1_driver = { + .name = DRIVER_NAME, + .probe = pt1_probe, + .remove = __devexit_p(pt1_remove), + .id_table = pt1_id_table, +}; + + +static int __init pt1_init(void) +{ + return pci_register_driver(&pt1_driver); +} + + +static void __exit pt1_cleanup(void) +{ + pci_unregister_driver(&pt1_driver); +} + +module_init(pt1_init); +module_exit(pt1_cleanup); + +MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>"); +MODULE_DESCRIPTION("Earthsoft PT1 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c new file mode 100644 index 00000000000..2db940f8635 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007s.c @@ -0,0 +1,658 @@ +/* + * ISDB-S driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "dvb_frontend.h" +#include "va1j5jf8007s.h" + +enum va1j5jf8007s_tune_state { + VA1J5JF8007S_IDLE, + VA1J5JF8007S_SET_FREQUENCY_1, + VA1J5JF8007S_SET_FREQUENCY_2, + VA1J5JF8007S_SET_FREQUENCY_3, + VA1J5JF8007S_CHECK_FREQUENCY, + VA1J5JF8007S_SET_MODULATION, + VA1J5JF8007S_CHECK_MODULATION, + VA1J5JF8007S_SET_TS_ID, + VA1J5JF8007S_CHECK_TS_ID, + VA1J5JF8007S_TRACK, +}; + +struct va1j5jf8007s_state { + const struct va1j5jf8007s_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007s_tune_state tune_state; +}; + +static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + case VA1J5JF8007S_SET_FREQUENCY_1: + case VA1J5JF8007S_SET_FREQUENCY_2: + case VA1J5JF8007S_SET_FREQUENCY_3: + case VA1J5JF8007S_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007S_SET_MODULATION: + case VA1J5JF8007S_CHECK_MODULATION: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + case VA1J5JF8007S_CHECK_TS_ID: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007s_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { + { 986000, 0xb2 }, + { 1072000, 0xd2 }, + { 1154000, 0xe2 }, + { 1291000, 0x20 }, + { 1447000, 0x40 }, + { 1615000, 0x60 }, + { 1791000, 0x80 }, + { 1972000, 0xa0 }, +}; + +static u8 va1j5jf8007s_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007s_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { + map = &va1j5jf8007s_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xc0; +} + +static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 500) / 1000; + if (frequency < 1072000) + word = (word << 1 & ~0x1f) | (word & 0x0f); + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0x40 | word >> 8; + buf[3] = word; + buf[4] = 0xe0; + buf[5] = va1j5jf8007s_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) +{ + u8 buf[3]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xe4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u8 buf[4]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf4; + buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc1; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = 0x01; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + return 0; +} + +static int +va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) +{ + u32 ts_id; + u8 buf[3]; + struct i2c_msg msg; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) + return 0; + + buf[0] = 0x8f; + buf[1] = ts_id >> 8; + buf[2] = ts_id; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[2]; + struct i2c_msg msgs[2]; + u32 ts_id; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) { + *lock = 1; + return 0; + } + + addr = state->config->demod_address; + + write_buf[0] = 0xe6; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; + return 0; +} + +static int +va1j5jf8007s_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + int ret; + int lock; + + state = fe->demodulator_priv; + + if (params != NULL) + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_1: + ret = va1j5jf8007s_set_frequency_1(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_2: + ret = va1j5jf8007s_set_frequency_2(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; + *delay = (HZ + 99) / 100; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_3: + ret = va1j5jf8007s_set_frequency_3(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_CHECK_FREQUENCY: + ret = va1j5jf8007s_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_MODULATION: + ret = va1j5jf8007s_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_CHECK_MODULATION: + ret = va1j5jf8007s_check_modulation(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 49) / 50; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_TS_ID; + *delay = 0; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + ret = va1j5jf8007s_set_ts_id(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_TS_ID; + return 0; + + case VA1J5JF8007S_CHECK_TS_ID: + ret = va1j5jf8007s_check_ts_id(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 99) / 100; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + } + + state->tune_state = VA1J5JF8007S_TRACK; + /* fall through */ + + case VA1J5JF8007S_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) +{ + u8 buf[4]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf0; + buf[3] = 0x04; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x17; + buf[1] = sleep ? 0x01 : 0x00; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_set_sleep(state, 1); +} + +static int va1j5jf8007s_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007S_IDLE; + + return va1j5jf8007s_set_sleep(state, 0); +} + +static void va1j5jf8007s_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007s_ops = { + .info = { + .name = "VA1J5JF8007 ISDB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .get_frontend_algo = va1j5jf8007s_get_frontend_algo, + .read_status = va1j5jf8007s_read_status, + .tune = va1j5jf8007s_tune, + .sleep = va1j5jf8007s_sleep, + .init = va1j5jf8007s_init, + .release = va1j5jf8007s_release, +}; + +static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x07; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + if (read_buf[0] != 0x41) + return -EIO; + + return 0; +} + +static const u8 va1j5jf8007s_prepare_bufs[][2] = { + {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, + {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, + {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, + {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) +{ + u8 addr; + u8 buf[2]; + struct i2c_msg msg; + int i; + + addr = state->config->demod_address; + + msg.addr = addr; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_prepare_bufs); i++) { + memcpy(buf, va1j5jf8007s_prepare_bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return 0; +} + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_prepare_1(state); + if (ret < 0) + return ret; + + ret = va1j5jf8007s_prepare_2(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007s_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/dvb/pt1/va1j5jf8007s.h new file mode 100644 index 00000000000..aa228a81635 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007s.h @@ -0,0 +1,40 @@ +/* + * ISDB-S driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> + * + * 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 VA1J5JF8007S_H +#define VA1J5JF8007S_H + +struct va1j5jf8007s_config { + u8 demod_address; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe); + +#endif diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c new file mode 100644 index 00000000000..71117f4ca7e --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007t.c @@ -0,0 +1,468 @@ +/* + * ISDB-T driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "dvb_frontend.h" +#include "dvb_math.h" +#include "va1j5jf8007t.h" + +enum va1j5jf8007t_tune_state { + VA1J5JF8007T_IDLE, + VA1J5JF8007T_SET_FREQUENCY, + VA1J5JF8007T_CHECK_FREQUENCY, + VA1J5JF8007T_SET_MODULATION, + VA1J5JF8007T_CHECK_MODULATION, + VA1J5JF8007T_TRACK, + VA1J5JF8007T_ABORT, +}; + +struct va1j5jf8007t_state { + const struct va1j5jf8007t_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007t_tune_state tune_state; +}; + +static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + case VA1J5JF8007T_SET_FREQUENCY: + case VA1J5JF8007T_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007T_SET_MODULATION: + case VA1J5JF8007T_CHECK_MODULATION: + case VA1J5JF8007T_ABORT: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007t_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { + { 90000000, 0x80 }, + { 140000000, 0x81 }, + { 170000000, 0xa1 }, + { 220000000, 0x62 }, + { 330000000, 0xa2 }, + { 402000000, 0xe2 }, + { 450000000, 0x64 }, + { 550000000, 0x84 }, + { 600000000, 0xa4 }, + { 700000000, 0xc4 }, +}; + +static u8 va1j5jf8007t_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007t_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { + map = &va1j5jf8007t_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xe4; +} + +static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 71428) / 142857 + 399; + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = word >> 8; + buf[3] = word; + buf[4] = 0x80; + buf[5] = va1j5jf8007t_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x01; + buf[1] = 0x40; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, + int *lock, int *retry) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x80; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + *retry = read_buf[0] & 0x80; + return 0; +} + +static int +va1j5jf8007t_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + int ret; + int lock, retry; + + state = fe->demodulator_priv; + + if (params != NULL) + state->tune_state = VA1J5JF8007T_SET_FREQUENCY; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007T_SET_FREQUENCY: + ret = va1j5jf8007t_set_frequency(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007T_CHECK_FREQUENCY: + ret = va1j5jf8007t_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007T_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_SET_MODULATION: + ret = va1j5jf8007t_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_CHECK_MODULATION: + ret = va1j5jf8007t_check_modulation(state, &lock, &retry); + if (ret < 0) + return ret; + + if (!lock) { + if (!retry) { + state->tune_state = VA1J5JF8007T_ABORT; + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + *delay = (HZ + 999) / 1000; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007T_TRACK; + /* fall through */ + + case VA1J5JF8007T_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + + case VA1J5JF8007T_ABORT: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + + BUG(); +} + +static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) +{ + u8 buf[7]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = 0x01; + buf[3] = 0x8f; + buf[4] = 0xc1; + buf[5] = 0x80; + buf[6] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = sleep ? 0x90 : 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007t_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007t_set_sleep(state, 1); +} + +static int va1j5jf8007t_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007T_IDLE; + + return va1j5jf8007t_set_sleep(state, 0); +} + +static void va1j5jf8007t_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007t_ops = { + .info = { + .name = "VA1J5JF8007 ISDB-T", + .type = FE_OFDM, + .frequency_min = 90000000, + .frequency_max = 770000000, + .frequency_stepsize = 142857, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .get_frontend_algo = va1j5jf8007t_get_frontend_algo, + .read_status = va1j5jf8007t_read_status, + .tune = va1j5jf8007t_tune, + .sleep = va1j5jf8007t_sleep, + .init = va1j5jf8007t_init, + .release = va1j5jf8007t_release, +}; + +static const u8 va1j5jf8007t_prepare_bufs[][2] = { + {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, + {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, + {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, + {0xef, 0x01} +}; + +int va1j5jf8007t_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + u8 buf[2]; + struct i2c_msg msg; + int i; + + state = fe->demodulator_priv; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_prepare_bufs); i++) { + memcpy(buf, va1j5jf8007t_prepare_bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return va1j5jf8007t_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007t_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/dvb/pt1/va1j5jf8007t.h new file mode 100644 index 00000000000..ed49906f776 --- /dev/null +++ b/drivers/media/dvb/pt1/va1j5jf8007t.h @@ -0,0 +1,40 @@ +/* + * ISDB-T driver for VA1J5JF8007 + * + * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> + * + * 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 VA1J5JF8007T_H +#define VA1J5JF8007T_H + +struct va1j5jf8007t_config { + u8 demod_address; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007s_attach */ +int va1j5jf8007t_prepare(struct dvb_frontend *fe); + +#endif diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig index dd863f26167..8c1aed77ea3 100644 --- a/drivers/media/dvb/siano/Kconfig +++ b/drivers/media/dvb/siano/Kconfig @@ -2,25 +2,33 @@ # Siano Mobile Silicon Digital TV device configuration # -config DVB_SIANO_SMS1XXX - tristate "Siano SMS1XXX USB dongle support" - depends on DVB_CORE && USB +config SMS_SIANO_MDTV + tristate "Siano SMS1xxx based MDTV receiver" + depends on DVB_CORE && INPUT ---help--- - Choose Y here if you have a USB dongle with a SMS1XXX chipset. + Choose Y or M here if you have MDTV receiver with a Siano chipset. - To compile this driver as a module, choose M here: the - module will be called sms1xxx. + To compile this driver as a module, choose M here + (The module will be called smsmdtv). -config DVB_SIANO_SMS1XXX_SMS_IDS - bool "Enable support for Siano Mobile Silicon default USB IDs" - depends on DVB_SIANO_SMS1XXX - default y - ---help--- - Choose Y here if you have a USB dongle with a SMS1XXX chipset - that uses Siano Mobile Silicon's default usb vid:pid. + Further documentation on this driver can be found on the WWW + at http://www.siano-ms.com/ + +if SMS_SIANO_MDTV +menu "Siano module components" - Choose N here if you would prefer to use Siano's external driver. +# Hardware interfaces support - Further documentation on this driver can be found on the WWW at - <http://www.siano-ms.com/>. +config SMS_USB_DRV + tristate "USB interface support" + depends on DVB_CORE && USB + ---help--- + Choose if you would like to have Siano's support for USB interface +config SMS_SDIO_DRV + tristate "SDIO interface support" + depends on DVB_CORE && MMC + ---help--- + Choose if you would like to have Siano's support for SDIO interface +endmenu +endif # SMS_SIANO_MDTV diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile index c6644d90943..c54140b5ab5 100644 --- a/drivers/media/dvb/siano/Makefile +++ b/drivers/media/dvb/siano/Makefile @@ -1,8 +1,9 @@ -sms1xxx-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o -obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o -obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsusb.o -obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsdvb.o +smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o + +obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o +obj-$(CONFIG_SMS_USB_DRV) += smsusb.o +obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index d8b15d583bd..0420e2885e7 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -116,99 +116,21 @@ static inline void sms_gpio_assign_11xx_default_led_config( int sms_board_event(struct smscore_device_t *coredev, enum SMS_BOARD_EVENTS gevent) { - int board_id = smscore_get_board_id(coredev); - struct sms_board *board = sms_get_board(board_id); struct smscore_gpio_config MyGpioConfig; sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); switch (gevent) { case BOARD_EVENT_POWER_INIT: /* including hotplug */ - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - /* set I/O and turn off all LEDs */ - smscore_gpio_configure(coredev, - board->board_cfg.leds_power, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 0); - smscore_gpio_configure(coredev, board->board_cfg.led0, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 0); - smscore_gpio_configure(coredev, board->board_cfg.led1, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - /* set I/O and turn off LNA */ - smscore_gpio_configure(coredev, - board->board_cfg.foreign_lna0_ctrl, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 0); - break; - } break; /* BOARD_EVENT_BIND */ case BOARD_EVENT_POWER_SUSPEND: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 0); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 0); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 0); - break; - } break; /* BOARD_EVENT_POWER_SUSPEND */ case BOARD_EVENT_POWER_RESUME: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 1); - break; - } break; /* BOARD_EVENT_POWER_RESUME */ case BOARD_EVENT_BIND: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 1); - break; - } break; /* BOARD_EVENT_BIND */ case BOARD_EVENT_SCAN_PROG: @@ -218,20 +140,8 @@ int sms_board_event(struct smscore_device_t *coredev, case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL: break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */ case BOARD_EVENT_FE_LOCK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 1); - break; - } break; /* BOARD_EVENT_FE_LOCK */ case BOARD_EVENT_FE_UNLOCK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - } break; /* BOARD_EVENT_FE_UNLOCK */ case BOARD_EVENT_DEMOD_LOCK: break; /* BOARD_EVENT_DEMOD_LOCK */ @@ -248,20 +158,8 @@ int sms_board_event(struct smscore_device_t *coredev, case BOARD_EVENT_RECEPTION_LOST_0: break; /* BOARD_EVENT_RECEPTION_LOST_0 */ case BOARD_EVENT_MULTIPLEX_OK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 1); - break; - } break; /* BOARD_EVENT_MULTIPLEX_OK */ case BOARD_EVENT_MULTIPLEX_ERRORS: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - } break; /* BOARD_EVENT_MULTIPLEX_ERRORS */ default: diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index a246903c334..fa6a62369a7 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) sms_debug("set device mode to %d", mode); if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) { + if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { sms_err("invalid mode specified %d", mode); return -EINVAL; } @@ -1367,7 +1367,7 @@ int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) &msg, sizeof(msg)); } -/* new GPIO managment implementation */ +/* new GPIO management implementation */ static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, u32 *pGroupNum, u32 *pGroupCfg) { diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h index f1108c64e89..eec18aaf551 100644 --- a/drivers/media/dvb/siano/smscoreapi.h +++ b/drivers/media/dvb/siano/smscoreapi.h @@ -657,12 +657,12 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); extern void smscore_putbuffer(struct smscore_device_t *coredev, struct smscore_buffer_t *cb); -/* old GPIO managment */ +/* old GPIO management */ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, struct smscore_config_gpio *pinconfig); int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); -/* new GPIO managment */ +/* new GPIO management */ extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, struct smscore_gpio_config *pGpioConfig); extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 3ee1c3902c5..266033ae278 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -325,6 +325,16 @@ static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, 0 : -ETIME; } +static inline int led_feedback(struct smsdvb_client_t *client) +{ + if (client->fe_status & FE_HAS_LOCK) + return sms_board_led_feedback(client->coredev, + (client->sms_stat_dvb.ReceptionData.BER + == 0) ? SMS_LED_HI : SMS_LED_LO); + else + return sms_board_led_feedback(client->coredev, SMS_LED_OFF); +} + static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) { struct smsdvb_client_t *client; @@ -332,6 +342,8 @@ static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) *stat = client->fe_status; + led_feedback(client); + return 0; } @@ -342,6 +354,8 @@ static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) *ber = client->sms_stat_dvb.ReceptionData.BER; + led_feedback(client); + return 0; } @@ -359,6 +373,8 @@ static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) (client->sms_stat_dvb.ReceptionData.InBandPwr + 95) * 3 / 2; + led_feedback(client); + return 0; } @@ -369,6 +385,8 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) *snr = client->sms_stat_dvb.ReceptionData.SNR; + led_feedback(client); + return 0; } @@ -379,6 +397,8 @@ static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; + led_feedback(client); + return 0; } @@ -404,6 +424,8 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe, u32 Data[3]; } Msg; + int ret; + client->fe_status = FE_HAS_SIGNAL; client->event_fe_state = -1; client->event_unc_state = -1; @@ -426,6 +448,23 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe, case BANDWIDTH_AUTO: return -EOPNOTSUPP; default: return -EINVAL; } + /* Disable LNA, if any. An error is returned if no LNA is present */ + ret = sms_board_lna_control(client->coredev, 0); + if (ret == 0) { + fe_status_t status; + + /* tune with LNA off at first */ + ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_done); + + smsdvb_read_status(fe, &status); + + if (status & FE_HAS_LOCK) + return ret; + + /* previous tune didnt lock - enable LNA and tune again */ + sms_board_lna_control(client->coredev, 1); + } return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); @@ -451,6 +490,8 @@ static int smsdvb_init(struct dvb_frontend *fe) struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); + sms_board_power(client->coredev, 1); + sms_board_dvb3_event(client, DVB3_EVENT_INIT); return 0; } @@ -460,6 +501,9 @@ static int smsdvb_sleep(struct dvb_frontend *fe) struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); + sms_board_led_feedback(client->coredev, SMS_LED_OFF); + sms_board_power(client->coredev, 0); + sms_board_dvb3_event(client, DVB3_EVENT_SLEEP); return 0; diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c index dfaa49a53f3..d1d652e7f89 100644 --- a/drivers/media/dvb/siano/smssdio.c +++ b/drivers/media/dvb/siano/smssdio.c @@ -46,6 +46,7 @@ #define SMSSDIO_DATA 0x00 #define SMSSDIO_INT 0x04 +#define SMSSDIO_BLOCK_SIZE 128 static const struct sdio_device_id smssdio_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), @@ -85,7 +86,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size) sdio_claim_host(smsdev->func); while (size >= smsdev->func->cur_blksize) { - ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1); + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, smsdev->func->cur_blksize); if (ret) goto out; @@ -94,8 +96,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size) } if (size) { - ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA, - buffer, size); + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, size); } out: @@ -125,23 +127,23 @@ static void smssdio_interrupt(struct sdio_func *func) */ isr = sdio_readb(func, SMSSDIO_INT, &ret); if (ret) { - dev_err(&smsdev->func->dev, - "Unable to read interrupt register!\n"); + sms_err("Unable to read interrupt register!\n"); return; } if (smsdev->split_cb == NULL) { cb = smscore_getbuffer(smsdev->coredev); if (!cb) { - dev_err(&smsdev->func->dev, - "Unable to allocate data buffer!\n"); + sms_err("Unable to allocate data buffer!\n"); return; } - ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1); + ret = sdio_memcpy_fromio(smsdev->func, + cb->p, + SMSSDIO_DATA, + SMSSDIO_BLOCK_SIZE); if (ret) { - dev_err(&smsdev->func->dev, - "Error %d reading initial block!\n", ret); + sms_err("Error %d reading initial block!\n", ret); return; } @@ -152,7 +154,10 @@ static void smssdio_interrupt(struct sdio_func *func) return; } - size = hdr->msgLength - smsdev->func->cur_blksize; + if (hdr->msgLength > smsdev->func->cur_blksize) + size = hdr->msgLength - smsdev->func->cur_blksize; + else + size = 0; } else { cb = smsdev->split_cb; hdr = cb->p; @@ -162,23 +167,24 @@ static void smssdio_interrupt(struct sdio_func *func) smsdev->split_cb = NULL; } - if (hdr->msgLength > smsdev->func->cur_blksize) { + if (size) { void *buffer; - size = ALIGN(size, 128); - buffer = cb->p + hdr->msgLength; + buffer = cb->p + (hdr->msgLength - size); + size = ALIGN(size, SMSSDIO_BLOCK_SIZE); - BUG_ON(smsdev->func->cur_blksize != 128); + BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); /* * First attempt to transfer all of it in one go... */ - ret = sdio_read_blocks(smsdev->func, buffer, - SMSSDIO_DATA, size / 128); + ret = sdio_memcpy_fromio(smsdev->func, + buffer, + SMSSDIO_DATA, + size); if (ret && ret != -EINVAL) { smscore_putbuffer(smsdev->coredev, cb); - dev_err(&smsdev->func->dev, - "Error %d reading data from card!\n", ret); + sms_err("Error %d reading data from card!\n", ret); return; } @@ -191,12 +197,12 @@ static void smssdio_interrupt(struct sdio_func *func) */ if (ret == -EINVAL) { while (size) { - ret = sdio_read_blocks(smsdev->func, - buffer, SMSSDIO_DATA, 1); + ret = sdio_memcpy_fromio(smsdev->func, + buffer, SMSSDIO_DATA, + smsdev->func->cur_blksize); if (ret) { smscore_putbuffer(smsdev->coredev, cb); - dev_err(&smsdev->func->dev, - "Error %d reading " + sms_err("Error %d reading " "data from card!\n", ret); return; } @@ -269,7 +275,7 @@ static int smssdio_probe(struct sdio_func *func, if (ret) goto release; - ret = sdio_set_block_size(func, 128); + ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); if (ret) goto disable; diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index ce64c6214cc..8986d967d2f 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -490,7 +490,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) if (!av7110->analog_tuner_flags) return 0; - if (input < 0 || input >= 4) + if (input >= 4) return -EINVAL; av7110->current_input = input; diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 371a7161681..b5c681372b6 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -225,7 +225,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1012: /* The hauppauge keymap is a superset of these remotes */ ir_input_init(input_dev, &budget_ci->ir.state, - IR_TYPE_RC5, ir_codes_hauppauge_new); + IR_TYPE_RC5, &ir_codes_hauppauge_new_table); if (rc5_device < 0) budget_ci->ir.rc5_device = 0x1f; @@ -237,7 +237,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x101a: /* for the Technotrend 1500 bundled remote */ ir_input_init(input_dev, &budget_ci->ir.state, - IR_TYPE_RC5, ir_codes_tt_1500); + IR_TYPE_RC5, &ir_codes_tt_1500_table); if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; @@ -247,7 +247,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) default: /* unknown remote */ ir_input_init(input_dev, &budget_ci->ir.state, - IR_TYPE_RC5, ir_codes_budget_ci_old); + IR_TYPE_RC5, &ir_codes_budget_ci_old_table); if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 3315cac875e..a87a477c87f 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -288,16 +288,6 @@ config RADIO_TYPHOON To compile this driver as a module, choose M here: the module will be called radio-typhoon. -config RADIO_TYPHOON_PROC_FS - bool "Support for /proc/radio-typhoon" - depends on PROC_FS && RADIO_TYPHOON - help - Say Y here if you want the typhoon radio card driver to write - status information (frequency, volume, muted, mute frequency, - base address) to /proc/radio-typhoon. The file can be viewed with - your favorite pager (i.e. use "more /proc/radio-typhoon" or "less - /proc/radio-typhoon" or simply "cat /proc/radio-typhoon"). - config RADIO_TYPHOON_PORT hex "Typhoon I/O port (0x316 or 0x336)" depends on RADIO_TYPHOON=y @@ -339,6 +329,29 @@ config RADIO_ZOLTRIX_PORT help Enter the I/O port of your Zoltrix radio card. +config I2C_SI4713 + tristate "I2C driver for Silicon Labs Si4713 device" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want support to Si4713 I2C device. + This device driver supports only i2c bus. + + To compile this driver as a module, choose M here: the + module will be called si4713. + +config RADIO_SI4713 + tristate "Silicon Labs Si4713 FM Radio Transmitter support" + depends on I2C && VIDEO_V4L2 + select I2C_SI4713 + ---help--- + Say Y here if you want support to Si4713 FM Radio Transmitter. + This device can transmit audio through FM. It can transmit + RDS and RBDS signals as well. This module is the v4l2 radio + interface for the i2c driver of this device. + + To compile this driver as a module, choose M here: the + module will be called radio-si4713. + config USB_DSBR tristate "D-Link/GemTek USB FM radio support" depends on USB && VIDEO_V4L2 @@ -351,29 +364,11 @@ config USB_DSBR To compile this driver as a module, choose M here: the module will be called dsbr100. -config USB_SI470X - tristate "Silicon Labs Si470x FM Radio Receiver support" - depends on USB && VIDEO_V4L2 - ---help--- - This is a driver for USB devices with the Silicon Labs SI470x - chip. Currently these devices are known to work: - - 10c4:818a: Silicon Labs USB FM Radio Reference Design - - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) - - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) - - Sound is provided by the ALSA USB Audio/MIDI driver. Therefore - if you don't want to use the device solely for RDS receiving, - it is recommended to also select SND_USB_AUDIO. - - Please have a look at the documentation, especially on how - to redirect the audio stream from the radio to your sound device: - Documentation/video4linux/si470x.txt - - Say Y here if you want to connect this type of radio to your - computer's USB port. +config RADIO_SI470X + bool "Silicon Labs Si470x FM Radio Receiver support" + depends on VIDEO_V4L2 - To compile this driver as a module, choose M here: the - module will be called radio-si470x. +source "drivers/media/radio/si470x/Kconfig" config USB_MR800 tristate "AverMedia MR 800 USB FM radio support" diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 0f2b35b3e56..2a1be3bf4f7 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -15,9 +15,11 @@ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o +obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o +obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o obj-$(CONFIG_USB_DSBR) += dsbr100.o -obj-$(CONFIG_USB_SI470X) += radio-si470x.o +obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index d30fc0ce82c..8b1440136c4 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -359,7 +359,8 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(v->card, "ADS Cadet", sizeof(v->card)); strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); v->version = CADET_VERSION; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_READWRITE; + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; return 0; } @@ -372,7 +373,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, switch (v->index) { case 0: strlcpy(v->name, "FM", sizeof(v->name)); - v->capability = V4L2_TUNER_CAP_STEREO; + v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS; v->rangelow = 1400; /* 87.5 MHz */ v->rangehigh = 1728; /* 108.0 MHz */ v->rxsubchans = cadet_getstereo(dev); @@ -386,6 +387,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, default: break; } + v->rxsubchans |= V4L2_TUNER_SUB_RDS; break; case 1: strlcpy(v->name, "AM", sizeof(v->name)); diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 575bf9d8941..a1239083472 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -46,7 +46,7 @@ * Version 0.11: Converted to v4l2_device. * * Many things to do: - * - Correct power managment of device (suspend & resume) + * - Correct power management of device (suspend & resume) * - Add code for scanning and smooth tuning * - Add code for sensitivity value * - Correct mistakes diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c deleted file mode 100644 index e85f318b4d2..00000000000 --- a/drivers/media/radio/radio-si470x.c +++ /dev/null @@ -1,1863 +0,0 @@ -/* - * drivers/media/radio/radio-si470x.c - * - * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers: - * - Silicon Labs USB FM Radio Reference Design - * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) - * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700) - * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) - * - * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* - * History: - * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.0 - * - First working version - * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.1 - * - Improved error handling, every function now returns errno - * - Improved multi user access (start/mute/stop) - * - Channel doesn't get lost anymore after start/mute/stop - * - RDS support added (polling mode via interrupt EP 1) - * - marked default module parameters with *value* - * - switched from bit structs to bit masks - * - header file cleaned and integrated - * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.2 - * - hex values are now lower case - * - commented USB ID for ADS/Tech moved on todo list - * - blacklisted si470x in hid-quirks.c - * - rds buffer handling functions integrated into *_work, *_read - * - rds_command in si470x_poll exchanged against simple retval - * - check for firmware version 15 - * - code order and prototypes still remain the same - * - spacing and bottom of band codes remain the same - * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.3 - * - code reordered to avoid function prototypes - * - switch/case defaults are now more user-friendly - * - unified comment style - * - applied all checkpatch.pl v1.12 suggestions - * except the warning about the too long lines with bit comments - * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) - * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.4 - * - avoid poss. locking when doing copy_to_user which may sleep - * - RDS is automatically activated on read now - * - code cleaned of unnecessary rds_commands - * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified - * (thanks to Guillaume RAMOUSSE) - * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.5 - * - number of seek_retries changed to tune_timeout - * - fixed problem with incomplete tune operations by own buffers - * - optimization of variables and printf types - * - improved error logging - * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net> - * Oliver Neukum <oliver@neukum.org> - * Version 1.0.6 - * - fixed coverity checker warnings in *_usb_driver_disconnect - * - probe()/open() race by correct ordering in probe() - * - DMA coherency rules by separate allocation of all buffers - * - use of endianness macros - * - abuse of spinlock, replaced by mutex - * - racy handling of timer in disconnect, - * replaced by delayed_work - * - racy interruptible_sleep_on(), - * replaced with wait_event_interruptible() - * - handle signals in read() - * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net> - * Oliver Neukum <oliver@neukum.org> - * Version 1.0.7 - * - usb autosuspend support - * - unplugging fixed - * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net> - * Version 1.0.8 - * - hardware frequency seek support - * - afc indication - * - more safety checks, let si470x_get_freq return errno - * - vidioc behavior corrected according to v4l2 spec - * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com> - * - add support for KWorld USB FM Radio FM700 - * - blacklisted KWorld radio in hid-core.c and hid-ids.h - * 2008-12-03 Mark Lord <mlord@pobox.com> - * - add support for DealExtreme USB Radio - * 2009-01-31 Bob Ross <pigiron@gmx.com> - * - correction of stereo detection/setting - * - correction of signal strength indicator scaling - * 2009-01-31 Rick Bronson <rick@efn.org> - * Tobias Lorenz <tobias.lorenz@gmx.net> - * - add LED status output - * - get HW/SW version from scratchpad - * - * ToDo: - * - add firmware download/update support - * - RDS support: interrupt mode, instead of polling - */ - - -/* driver definitions */ -#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" -#define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 9) -#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" -#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" -#define DRIVER_VERSION "1.0.9" - - -/* kernel includes */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/input.h> -#include <linux/usb.h> -#include <linux/hid.h> -#include <linux/version.h> -#include <linux/videodev2.h> -#include <linux/mutex.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/rds.h> -#include <asm/unaligned.h> - - -/* USB Device ID List */ -static struct usb_device_id si470x_usb_driver_id_table[] = { - /* Silicon Labs USB FM Radio Reference Design */ - { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, - /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, - /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, - /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, - /* Terminating entry */ - { } -}; -MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); - - - -/************************************************************************** - * Module Parameters - **************************************************************************/ - -/* Radio Nr */ -static int radio_nr = -1; -module_param(radio_nr, int, 0444); -MODULE_PARM_DESC(radio_nr, "Radio Nr"); - -/* Spacing (kHz) */ -/* 0: 200 kHz (USA, Australia) */ -/* 1: 100 kHz (Europe, Japan) */ -/* 2: 50 kHz */ -static unsigned short space = 2; -module_param(space, ushort, 0444); -MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); - -/* Bottom of Band (MHz) */ -/* 0: 87.5 - 108 MHz (USA, Europe)*/ -/* 1: 76 - 108 MHz (Japan wide band) */ -/* 2: 76 - 90 MHz (Japan) */ -static unsigned short band = 1; -module_param(band, ushort, 0444); -MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); - -/* De-emphasis */ -/* 0: 75 us (USA) */ -/* 1: 50 us (Europe, Australia, Japan) */ -static unsigned short de = 1; -module_param(de, ushort, 0444); -MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*"); - -/* USB timeout */ -static unsigned int usb_timeout = 500; -module_param(usb_timeout, uint, 0644); -MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); - -/* Tune timeout */ -static unsigned int tune_timeout = 3000; -module_param(tune_timeout, uint, 0644); -MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); - -/* Seek timeout */ -static unsigned int seek_timeout = 5000; -module_param(seek_timeout, uint, 0644); -MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); - -/* RDS buffer blocks */ -static unsigned int rds_buf = 100; -module_param(rds_buf, uint, 0444); -MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); - -/* RDS maximum block errors */ -static unsigned short max_rds_errors = 1; -/* 0 means 0 errors requiring correction */ -/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ -/* 2 means 3-5 errors requiring correction */ -/* 3 means 6+ errors or errors in checkword, correction not possible */ -module_param(max_rds_errors, ushort, 0644); -MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); - -/* RDS poll frequency */ -static unsigned int rds_poll_time = 40; -/* 40 is used by the original USBRadio.exe */ -/* 50 is used by radio-cadet */ -/* 75 should be okay */ -/* 80 is the usual RDS receive interval */ -module_param(rds_poll_time, uint, 0644); -MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); - - - -/************************************************************************** - * Register Definitions - **************************************************************************/ -#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */ -#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */ -#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */ - -#define DEVICEID 0 /* Device ID */ -#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ -#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ - -#define CHIPID 1 /* Chip ID */ -#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ -#define CHIPID_DEV 0x0200 /* bits 09..09: Device */ -#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ - -#define POWERCFG 2 /* Power Configuration */ -#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ -#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */ -#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */ -#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */ -#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */ -#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */ -#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */ -#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */ -#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */ - -#define CHANNEL 3 /* Channel */ -#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */ -#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */ - -#define SYSCONFIG1 4 /* System Configuration 1 */ -#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */ -#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */ -#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */ -#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */ -#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */ -#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ -#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ -#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ -#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ - -#define SYSCONFIG2 5 /* System Configuration 2 */ -#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ -#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ -#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ -#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ - -#define SYSCONFIG3 6 /* System Configuration 3 */ -#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */ -#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */ -#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */ -#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */ - -#define TEST1 7 /* Test 1 */ -#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */ - -#define TEST2 8 /* Test 2 */ -/* TEST2 only contains reserved bits */ - -#define BOOTCONFIG 9 /* Boot Configuration */ -/* BOOTCONFIG only contains reserved bits */ - -#define STATUSRSSI 10 /* Status RSSI */ -#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */ -#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */ -#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */ -#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */ -#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */ -#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */ -#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */ -#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */ - -#define READCHAN 11 /* Read Channel */ -#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */ -#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */ -#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */ -#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */ - -#define RDSA 12 /* RDSA */ -#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */ - -#define RDSB 13 /* RDSB */ -#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */ - -#define RDSC 14 /* RDSC */ -#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */ - -#define RDSD 15 /* RDSD */ -#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ - - - -/************************************************************************** - * USB HID Reports - **************************************************************************/ - -/* Reports 1-16 give direct read/write access to the 16 Si470x registers */ -/* with the (REPORT_ID - 1) corresponding to the register address across USB */ -/* endpoint 0 using GET_REPORT and SET_REPORT */ -#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) -#define REGISTER_REPORT(reg) ((reg) + 1) - -/* Report 17 gives direct read/write access to the entire Si470x register */ -/* map across endpoint 0 using GET_REPORT and SET_REPORT */ -#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) -#define ENTIRE_REPORT 17 - -/* Report 18 is used to send the lowest 6 Si470x registers up the HID */ -/* interrupt endpoint 1 to Windows every 20 milliseconds for status */ -#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) -#define RDS_REPORT 18 - -/* Report 19: LED state */ -#define LED_REPORT_SIZE 3 -#define LED_REPORT 19 - -/* Report 19: stream */ -#define STREAM_REPORT_SIZE 3 -#define STREAM_REPORT 19 - -/* Report 20: scratch */ -#define SCRATCH_PAGE_SIZE 63 -#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) -#define SCRATCH_REPORT 20 - -/* Reports 19-22: flash upgrade of the C8051F321 */ -#define WRITE_REPORT_SIZE 4 -#define WRITE_REPORT 19 -#define FLASH_REPORT_SIZE 64 -#define FLASH_REPORT 20 -#define CRC_REPORT_SIZE 3 -#define CRC_REPORT 21 -#define RESPONSE_REPORT_SIZE 2 -#define RESPONSE_REPORT 22 - -/* Report 23: currently unused, but can accept 60 byte reports on the HID */ -/* interrupt out endpoint 2 every 1 millisecond */ -#define UNUSED_REPORT 23 - - - -/************************************************************************** - * Software/Hardware Versions - **************************************************************************/ -#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 -#define RADIO_SW_VERSION 7 -#define RADIO_SW_VERSION_CURRENT 15 -#define RADIO_HW_VERSION 1 - -#define SCRATCH_PAGE_SW_VERSION 1 -#define SCRATCH_PAGE_HW_VERSION 2 - - - -/************************************************************************** - * LED State Definitions - **************************************************************************/ -#define LED_COMMAND 0x35 - -#define NO_CHANGE_LED 0x00 -#define ALL_COLOR_LED 0x01 /* streaming state */ -#define BLINK_GREEN_LED 0x02 /* connect state */ -#define BLINK_RED_LED 0x04 -#define BLINK_ORANGE_LED 0x10 /* disconnect state */ -#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ -#define SOLID_RED_LED 0x40 /* bootload state */ -#define SOLID_ORANGE_LED 0x80 - - - -/************************************************************************** - * Stream State Definitions - **************************************************************************/ -#define STREAM_COMMAND 0x36 -#define STREAM_VIDPID 0x00 -#define STREAM_AUDIO 0xff - - - -/************************************************************************** - * Bootloader / Flash Commands - **************************************************************************/ - -/* unique id sent to bootloader and required to put into a bootload state */ -#define UNIQUE_BL_ID 0x34 - -/* mask for the flash data */ -#define FLASH_DATA_MASK 0x55 - -/* bootloader commands */ -#define GET_SW_VERSION_COMMAND 0x00 -#define SET_PAGE_COMMAND 0x01 -#define ERASE_PAGE_COMMAND 0x02 -#define WRITE_PAGE_COMMAND 0x03 -#define CRC_ON_PAGE_COMMAND 0x04 -#define READ_FLASH_BYTE_COMMAND 0x05 -#define RESET_DEVICE_COMMAND 0x06 -#define GET_HW_VERSION_COMMAND 0x07 -#define BLANK 0xff - -/* bootloader command responses */ -#define COMMAND_OK 0x01 -#define COMMAND_FAILED 0x02 -#define COMMAND_PENDING 0x03 - - - -/************************************************************************** - * General Driver Definitions - **************************************************************************/ - -/* - * si470x_device - private data - */ -struct si470x_device { - /* reference to USB and video device */ - struct usb_device *usbdev; - struct usb_interface *intf; - struct video_device *videodev; - - /* driver management */ - unsigned int users; - unsigned char disconnected; - struct mutex disconnect_lock; - - /* Silabs internal registers (0..15) */ - unsigned short registers[RADIO_REGISTER_NUM]; - - /* RDS receive buffer */ - struct delayed_work work; - wait_queue_head_t read_queue; - struct mutex lock; /* buffer locking */ - unsigned char *buffer; /* size is always multiple of three */ - unsigned int buf_size; - unsigned int rd_index; - unsigned int wr_index; - - /* scratch page */ - unsigned char software_version; - unsigned char hardware_version; -}; - - -/* - * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, - * 62.5 kHz otherwise. - * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. - * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW - * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 - */ -#define FREQ_MUL (1000000 / 62.5) - - - -/************************************************************************** - * General Driver Functions - REGISTER_REPORTs - **************************************************************************/ - -/* - * si470x_get_report - receive a HID report - */ -static int si470x_get_report(struct si470x_device *radio, void *buf, int size) -{ - unsigned char *report = (unsigned char *) buf; - int retval; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - HID_REQ_GET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - report[0], 2, - buf, size, usb_timeout); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_get_report: usb_control_msg returned %d\n", - retval); - return retval; -} - - -/* - * si470x_set_report - send a HID report - */ -static int si470x_set_report(struct si470x_device *radio, void *buf, int size) -{ - unsigned char *report = (unsigned char *) buf; - int retval; - - retval = usb_control_msg(radio->usbdev, - usb_sndctrlpipe(radio->usbdev, 0), - HID_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - report[0], 2, - buf, size, usb_timeout); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_set_report: usb_control_msg returned %d\n", - retval); - return retval; -} - - -/* - * si470x_get_register - read register - */ -static int si470x_get_register(struct si470x_device *radio, int regnr) -{ - unsigned char buf[REGISTER_REPORT_SIZE]; - int retval; - - buf[0] = REGISTER_REPORT(regnr); - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval >= 0) - radio->registers[regnr] = get_unaligned_be16(&buf[1]); - - return (retval < 0) ? -EINVAL : 0; -} - - -/* - * si470x_set_register - write register - */ -static int si470x_set_register(struct si470x_device *radio, int regnr) -{ - unsigned char buf[REGISTER_REPORT_SIZE]; - int retval; - - buf[0] = REGISTER_REPORT(regnr); - put_unaligned_be16(radio->registers[regnr], &buf[1]); - - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); - - return (retval < 0) ? -EINVAL : 0; -} - - -/* - * si470x_set_chan - set the channel - */ -static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) -{ - int retval; - unsigned long timeout; - bool timed_out = 0; - - /* start tuning */ - radio->registers[CHANNEL] &= ~CHANNEL_CHAN; - radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; - retval = si470x_set_register(radio, CHANNEL); - if (retval < 0) - goto done; - - /* wait till tune operation has completed */ - timeout = jiffies + msecs_to_jiffies(tune_timeout); - do { - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto stop; - timed_out = time_after(jiffies, timeout); - } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && - (!timed_out)); - if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); - if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": tune timed out after %u ms\n", tune_timeout); - -stop: - /* stop tuning */ - radio->registers[CHANNEL] &= ~CHANNEL_TUNE; - retval = si470x_set_register(radio, CHANNEL); - -done: - return retval; -} - - -/* - * si470x_get_freq - get the frequency - */ -static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) -{ - unsigned int spacing, band_bottom; - unsigned short chan; - int retval; - - /* Spacing (kHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { - /* 0: 200 kHz (USA, Australia) */ - case 0: - spacing = 0.200 * FREQ_MUL; break; - /* 1: 100 kHz (Europe, Japan) */ - case 1: - spacing = 0.100 * FREQ_MUL; break; - /* 2: 50 kHz */ - default: - spacing = 0.050 * FREQ_MUL; break; - }; - - /* Bottom of Band (MHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe) */ - case 0: - band_bottom = 87.5 * FREQ_MUL; break; - /* 1: 76 - 108 MHz (Japan wide band) */ - default: - band_bottom = 76 * FREQ_MUL; break; - /* 2: 76 - 90 MHz (Japan) */ - case 2: - band_bottom = 76 * FREQ_MUL; break; - }; - - /* read channel */ - retval = si470x_get_register(radio, READCHAN); - chan = radio->registers[READCHAN] & READCHAN_READCHAN; - - /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ - *freq = chan * spacing + band_bottom; - - return retval; -} - - -/* - * si470x_set_freq - set the frequency - */ -static int si470x_set_freq(struct si470x_device *radio, unsigned int freq) -{ - unsigned int spacing, band_bottom; - unsigned short chan; - - /* Spacing (kHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { - /* 0: 200 kHz (USA, Australia) */ - case 0: - spacing = 0.200 * FREQ_MUL; break; - /* 1: 100 kHz (Europe, Japan) */ - case 1: - spacing = 0.100 * FREQ_MUL; break; - /* 2: 50 kHz */ - default: - spacing = 0.050 * FREQ_MUL; break; - }; - - /* Bottom of Band (MHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe) */ - case 0: - band_bottom = 87.5 * FREQ_MUL; break; - /* 1: 76 - 108 MHz (Japan wide band) */ - default: - band_bottom = 76 * FREQ_MUL; break; - /* 2: 76 - 90 MHz (Japan) */ - case 2: - band_bottom = 76 * FREQ_MUL; break; - }; - - /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ - chan = (freq - band_bottom) / spacing; - - return si470x_set_chan(radio, chan); -} - - -/* - * si470x_set_seek - set seek - */ -static int si470x_set_seek(struct si470x_device *radio, - unsigned int wrap_around, unsigned int seek_upward) -{ - int retval = 0; - unsigned long timeout; - bool timed_out = 0; - - /* start seeking */ - radio->registers[POWERCFG] |= POWERCFG_SEEK; - if (wrap_around == 1) - radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; - else - radio->registers[POWERCFG] |= POWERCFG_SKMODE; - if (seek_upward == 1) - radio->registers[POWERCFG] |= POWERCFG_SEEKUP; - else - radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; - retval = si470x_set_register(radio, POWERCFG); - if (retval < 0) - goto done; - - /* wait till seek operation has completed */ - timeout = jiffies + msecs_to_jiffies(seek_timeout); - do { - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto stop; - timed_out = time_after(jiffies, timeout); - } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && - (!timed_out)); - if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); - if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) - printk(KERN_WARNING DRIVER_NAME - ": seek failed / band limit reached\n"); - if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": seek timed out after %u ms\n", seek_timeout); - -stop: - /* stop seeking */ - radio->registers[POWERCFG] &= ~POWERCFG_SEEK; - retval = si470x_set_register(radio, POWERCFG); - -done: - /* try again, if timed out */ - if ((retval == 0) && timed_out) - retval = -EAGAIN; - - return retval; -} - - -/* - * si470x_start - switch on radio - */ -static int si470x_start(struct si470x_device *radio) -{ - int retval; - - /* powercfg */ - radio->registers[POWERCFG] = - POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; - retval = si470x_set_register(radio, POWERCFG); - if (retval < 0) - goto done; - - /* sysconfig 1 */ - radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - goto done; - - /* sysconfig 2 */ - radio->registers[SYSCONFIG2] = - (0x3f << 8) | /* SEEKTH */ - ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ - ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ - 15; /* VOLUME (max) */ - retval = si470x_set_register(radio, SYSCONFIG2); - if (retval < 0) - goto done; - - /* reset last channel */ - retval = si470x_set_chan(radio, - radio->registers[CHANNEL] & CHANNEL_CHAN); - -done: - return retval; -} - - -/* - * si470x_stop - switch off radio - */ -static int si470x_stop(struct si470x_device *radio) -{ - int retval; - - /* sysconfig 1 */ - radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - goto done; - - /* powercfg */ - radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; - /* POWERCFG_ENABLE has to automatically go low */ - radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; - retval = si470x_set_register(radio, POWERCFG); - -done: - return retval; -} - - -/* - * si470x_rds_on - switch on rds reception - */ -static int si470x_rds_on(struct si470x_device *radio) -{ - int retval; - - /* sysconfig 1 */ - mutex_lock(&radio->lock); - radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; - mutex_unlock(&radio->lock); - - return retval; -} - - - -/************************************************************************** - * General Driver Functions - ENTIRE_REPORT - **************************************************************************/ - -/* - * si470x_get_all_registers - read entire registers - */ -static int si470x_get_all_registers(struct si470x_device *radio) -{ - unsigned char buf[ENTIRE_REPORT_SIZE]; - int retval; - unsigned char regnr; - - buf[0] = ENTIRE_REPORT; - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval >= 0) - for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * General Driver Functions - RDS_REPORT - **************************************************************************/ - -/* - * si470x_get_rds_registers - read rds registers - */ -static int si470x_get_rds_registers(struct si470x_device *radio) -{ - unsigned char buf[RDS_REPORT_SIZE]; - int retval; - int size; - unsigned char regnr; - - buf[0] = RDS_REPORT; - - retval = usb_interrupt_msg(radio->usbdev, - usb_rcvintpipe(radio->usbdev, 1), - (void *) &buf, sizeof(buf), &size, usb_timeout); - if (size != sizeof(buf)) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " - "return size differs: %d != %zu\n", size, sizeof(buf)); - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " - "usb_interrupt_msg returned %d\n", retval); - - if (retval >= 0) - for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) - radio->registers[STATUSRSSI + regnr] = - get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * General Driver Functions - LED_REPORT - **************************************************************************/ - -/* - * si470x_set_led_state - sets the led state - */ -static int si470x_set_led_state(struct si470x_device *radio, - unsigned char led_state) -{ - unsigned char buf[LED_REPORT_SIZE]; - int retval; - - buf[0] = LED_REPORT; - buf[1] = LED_COMMAND; - buf[2] = led_state; - - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * General Driver Functions - SCRATCH_REPORT - **************************************************************************/ - -/* - * si470x_get_scratch_versions - gets the scratch page and version infos - */ -static int si470x_get_scratch_page_versions(struct si470x_device *radio) -{ - unsigned char buf[SCRATCH_REPORT_SIZE]; - int retval; - - buf[0] = SCRATCH_REPORT; - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: " - "si470x_get_report returned %d\n", retval); - else { - radio->software_version = buf[1]; - radio->hardware_version = buf[2]; - } - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * RDS Driver Functions - **************************************************************************/ - -/* - * si470x_rds - rds processing function - */ -static void si470x_rds(struct si470x_device *radio) -{ - unsigned char blocknum; - unsigned short bler; /* rds block errors */ - unsigned short rds; - unsigned char tmpbuf[3]; - - /* get rds blocks */ - if (si470x_get_rds_registers(radio) < 0) - return; - if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { - /* No RDS group ready */ - return; - } - if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { - /* RDS decoder not synchronized */ - return; - } - - /* copy all four RDS blocks to internal buffer */ - mutex_lock(&radio->lock); - for (blocknum = 0; blocknum < 4; blocknum++) { - switch (blocknum) { - default: - bler = (radio->registers[STATUSRSSI] & - STATUSRSSI_BLERA) >> 9; - rds = radio->registers[RDSA]; - break; - case 1: - bler = (radio->registers[READCHAN] & - READCHAN_BLERB) >> 14; - rds = radio->registers[RDSB]; - break; - case 2: - bler = (radio->registers[READCHAN] & - READCHAN_BLERC) >> 12; - rds = radio->registers[RDSC]; - break; - case 3: - bler = (radio->registers[READCHAN] & - READCHAN_BLERD) >> 10; - rds = radio->registers[RDSD]; - break; - }; - - /* Fill the V4L2 RDS buffer */ - put_unaligned_le16(rds, &tmpbuf); - tmpbuf[2] = blocknum; /* offset name */ - tmpbuf[2] |= blocknum << 3; /* received offset */ - if (bler > max_rds_errors) - tmpbuf[2] |= 0x80; /* uncorrectable errors */ - else if (bler > 0) - tmpbuf[2] |= 0x40; /* corrected error(s) */ - - /* copy RDS block to internal buffer */ - memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); - radio->wr_index += 3; - - /* wrap write pointer */ - if (radio->wr_index >= radio->buf_size) - radio->wr_index = 0; - - /* check for overflow */ - if (radio->wr_index == radio->rd_index) { - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - } - } - mutex_unlock(&radio->lock); - - /* wake up read queue */ - if (radio->wr_index != radio->rd_index) - wake_up_interruptible(&radio->read_queue); -} - - -/* - * si470x_work - rds work function - */ -static void si470x_work(struct work_struct *work) -{ - struct si470x_device *radio = container_of(work, struct si470x_device, - work.work); - - /* safety checks */ - if (radio->disconnected) - return; - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) - return; - - si470x_rds(radio); - schedule_delayed_work(&radio->work, msecs_to_jiffies(rds_poll_time)); -} - - - -/************************************************************************** - * File Operations Interface - **************************************************************************/ - -/* - * si470x_fops_read - read RDS data - */ -static ssize_t si470x_fops_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - unsigned int block_count = 0; - - /* switch on rds reception */ - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { - si470x_rds_on(radio); - schedule_delayed_work(&radio->work, - msecs_to_jiffies(rds_poll_time)); - } - - /* block if no new data available */ - while (radio->wr_index == radio->rd_index) { - if (file->f_flags & O_NONBLOCK) { - retval = -EWOULDBLOCK; - goto done; - } - if (wait_event_interruptible(radio->read_queue, - radio->wr_index != radio->rd_index) < 0) { - retval = -EINTR; - goto done; - } - } - - /* calculate block count from byte count */ - count /= 3; - - /* copy RDS block out of internal buffer and to user buffer */ - mutex_lock(&radio->lock); - while (block_count < count) { - if (radio->rd_index == radio->wr_index) - break; - - /* always transfer rds complete blocks */ - if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) - /* retval = -EFAULT; */ - break; - - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - - /* increment counters */ - block_count++; - buf += 3; - retval += 3; - } - mutex_unlock(&radio->lock); - -done: - return retval; -} - - -/* - * si470x_fops_poll - poll RDS data - */ -static unsigned int si470x_fops_poll(struct file *file, - struct poll_table_struct *pts) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* switch on rds reception */ - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { - si470x_rds_on(radio); - schedule_delayed_work(&radio->work, - msecs_to_jiffies(rds_poll_time)); - } - - poll_wait(file, &radio->read_queue, pts); - - if (radio->rd_index != radio->wr_index) - retval = POLLIN | POLLRDNORM; - - return retval; -} - - -/* - * si470x_fops_open - file open - */ -static int si470x_fops_open(struct file *file) -{ - struct si470x_device *radio = video_drvdata(file); - int retval; - - lock_kernel(); - radio->users++; - - retval = usb_autopm_get_interface(radio->intf); - if (retval < 0) { - radio->users--; - retval = -EIO; - goto done; - } - - if (radio->users == 1) { - /* start radio */ - retval = si470x_start(radio); - if (retval < 0) - usb_autopm_put_interface(radio->intf); - } - -done: - unlock_kernel(); - return retval; -} - - -/* - * si470x_fops_release - file release - */ -static int si470x_fops_release(struct file *file) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety check */ - if (!radio) { - retval = -ENODEV; - goto done; - } - - mutex_lock(&radio->disconnect_lock); - radio->users--; - if (radio->users == 0) { - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - goto unlock; - } - - /* stop rds reception */ - cancel_delayed_work_sync(&radio->work); - - /* cancel read processes */ - wake_up_interruptible(&radio->read_queue); - - /* stop radio */ - retval = si470x_stop(radio); - usb_autopm_put_interface(radio->intf); - } -unlock: - mutex_unlock(&radio->disconnect_lock); -done: - return retval; -} - - -/* - * si470x_fops - file operations interface - */ -static const struct v4l2_file_operations si470x_fops = { - .owner = THIS_MODULE, - .read = si470x_fops_read, - .poll = si470x_fops_poll, - .ioctl = video_ioctl2, - .open = si470x_fops_open, - .release = si470x_fops_release, -}; - - - -/************************************************************************** - * Video4Linux Interface - **************************************************************************/ - -/* - * si470x_v4l2_queryctrl - query control - */ -static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 15, - }, - { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, -}; - - -/* - * si470x_vidioc_querycap - query device capabilities - */ -static int si470x_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) -{ - struct si470x_device *radio = video_drvdata(file); - - strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); - strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); - usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info)); - capability->version = DRIVER_KERNEL_VERSION; - capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | - V4L2_CAP_TUNER | V4L2_CAP_RADIO; - - return 0; -} - - -/* - * si470x_vidioc_queryctrl - enumerate control items - */ -static int si470x_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - unsigned char i = 0; - int retval = -EINVAL; - - /* abort if qc->id is below V4L2_CID_BASE */ - if (qc->id < V4L2_CID_BASE) - goto done; - - /* search video control */ - for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) { - if (qc->id == si470x_v4l2_queryctrl[i].id) { - memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc)); - retval = 0; /* found */ - break; - } - } - - /* disable unsupported base controls */ - /* to satisfy kradio and such apps */ - if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { - qc->flags = V4L2_CTRL_FLAG_DISABLED; - retval = 0; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": query controls failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_ctrl - get the value of a control - */ -static int si470x_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = radio->registers[SYSCONFIG2] & - SYSCONFIG2_VOLUME; - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = ((radio->registers[POWERCFG] & - POWERCFG_DMUTE) == 0) ? 1 : 0; - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get control failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_ctrl - set the value of a control - */ -static int si470x_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; - radio->registers[SYSCONFIG2] |= ctrl->value; - retval = si470x_set_register(radio, SYSCONFIG2); - break; - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value == 1) - radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; - else - radio->registers[POWERCFG] |= POWERCFG_DMUTE; - retval = si470x_set_register(radio, POWERCFG); - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set control failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_audio - get audio attributes - */ -static int si470x_vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - /* driver constants */ - audio->index = 0; - strcpy(audio->name, "Radio"); - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - - return 0; -} - - -/* - * si470x_vidioc_g_tuner - get tuner attributes - */ -static int si470x_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (tuner->index != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto done; - - /* driver constants */ - strcpy(tuner->name, "FM"); - tuner->type = V4L2_TUNER_RADIO; - tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - - /* range limits */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe, default) */ - default: - tuner->rangelow = 87.5 * FREQ_MUL; - tuner->rangehigh = 108 * FREQ_MUL; - break; - /* 1: 76 - 108 MHz (Japan wide band) */ - case 1 : - tuner->rangelow = 76 * FREQ_MUL; - tuner->rangehigh = 108 * FREQ_MUL; - break; - /* 2: 76 - 90 MHz (Japan) */ - case 2 : - tuner->rangelow = 76 * FREQ_MUL; - tuner->rangehigh = 90 * FREQ_MUL; - break; - }; - - /* stereo indicator == stereo (instead of mono) */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) - tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - - /* mono/stereo selector */ - if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) - tuner->audmode = V4L2_TUNER_MODE_STEREO; - else - tuner->audmode = V4L2_TUNER_MODE_MONO; - - /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ - tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); - /* the ideal factor is 0xffff/75 = 873,8 */ - tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); - - /* automatic frequency control: -1: freq to low, 1 freq to high */ - /* AFCRL does only indicate that freq. differs, not if too low/high */ - tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get tuner failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_tuner - set tuner attributes - */ -static int si470x_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = -EINVAL; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (tuner->index != 0) - goto done; - - /* mono/stereo selector */ - switch (tuner->audmode) { - case V4L2_TUNER_MODE_MONO: - radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ - break; - case V4L2_TUNER_MODE_STEREO: - radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ - break; - default: - goto done; - } - - retval = si470x_set_register(radio, POWERCFG); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set tuner failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_frequency - get tuner or modulator radio frequency - */ -static int si470x_vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - - freq->type = V4L2_TUNER_RADIO; - retval = si470x_get_freq(radio, &freq->frequency); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get frequency failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_frequency - set tuner or modulator radio frequency - */ -static int si470x_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_freq(radio, freq->frequency); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set frequency failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek - */ -static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, - struct v4l2_hw_freq_seek *seek) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (seek->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set hardware frequency seek failed with %d\n", - retval); - return retval; -} - - -/* - * si470x_ioctl_ops - video device ioctl operations - */ -static const struct v4l2_ioctl_ops si470x_ioctl_ops = { - .vidioc_querycap = si470x_vidioc_querycap, - .vidioc_queryctrl = si470x_vidioc_queryctrl, - .vidioc_g_ctrl = si470x_vidioc_g_ctrl, - .vidioc_s_ctrl = si470x_vidioc_s_ctrl, - .vidioc_g_audio = si470x_vidioc_g_audio, - .vidioc_g_tuner = si470x_vidioc_g_tuner, - .vidioc_s_tuner = si470x_vidioc_s_tuner, - .vidioc_g_frequency = si470x_vidioc_g_frequency, - .vidioc_s_frequency = si470x_vidioc_s_frequency, - .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, -}; - - -/* - * si470x_viddev_template - video device interface - */ -static struct video_device si470x_viddev_template = { - .fops = &si470x_fops, - .name = DRIVER_NAME, - .release = video_device_release, - .ioctl_ops = &si470x_ioctl_ops, -}; - - - -/************************************************************************** - * USB Interface - **************************************************************************/ - -/* - * si470x_usb_driver_probe - probe for the device - */ -static int si470x_usb_driver_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct si470x_device *radio; - int retval = 0; - - /* private data allocation and initialization */ - radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); - if (!radio) { - retval = -ENOMEM; - goto err_initial; - } - radio->users = 0; - radio->disconnected = 0; - radio->usbdev = interface_to_usbdev(intf); - radio->intf = intf; - mutex_init(&radio->disconnect_lock); - mutex_init(&radio->lock); - - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; - goto err_radio; - } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); - - /* show some infos about the specific si470x device */ - if (si470x_get_all_registers(radio) < 0) { - retval = -EIO; - goto err_video; - } - printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", - radio->registers[DEVICEID], radio->registers[CHIPID]); - - /* get software and hardware versions */ - if (si470x_get_scratch_page_versions(radio) < 0) { - retval = -EIO; - goto err_video; - } - printk(KERN_INFO DRIVER_NAME - ": software version %d, hardware version %d\n", - radio->software_version, radio->hardware_version); - - /* check if device and firmware is current */ - if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) - < RADIO_SW_VERSION_CURRENT) { - printk(KERN_WARNING DRIVER_NAME - ": This driver is known to work with " - "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); - printk(KERN_WARNING DRIVER_NAME - ": but the device has firmware version %hu.\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE); - printk(KERN_WARNING DRIVER_NAME - ": If you have some trouble using this driver,\n"); - printk(KERN_WARNING DRIVER_NAME - ": please report to V4L ML at " - "linux-media@vger.kernel.org\n"); - } - - /* set initial frequency */ - si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ - - /* set led to connect state */ - si470x_set_led_state(radio, BLINK_GREEN_LED); - - /* rds buffer allocation */ - radio->buf_size = rds_buf * 3; - radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); - if (!radio->buffer) { - retval = -EIO; - goto err_video; - } - - /* rds buffer configuration */ - radio->wr_index = 0; - radio->rd_index = 0; - init_waitqueue_head(&radio->read_queue); - - /* prepare rds work function */ - INIT_DELAYED_WORK(&radio->work, si470x_work); - - /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); - if (retval) { - printk(KERN_WARNING DRIVER_NAME - ": Could not register video device\n"); - goto err_all; - } - usb_set_intfdata(intf, radio); - - return 0; -err_all: - kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); -err_radio: - kfree(radio); -err_initial: - return retval; -} - - -/* - * si470x_usb_driver_suspend - suspend the device - */ -static int si470x_usb_driver_suspend(struct usb_interface *intf, - pm_message_t message) -{ - struct si470x_device *radio = usb_get_intfdata(intf); - - printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); - - cancel_delayed_work_sync(&radio->work); - - return 0; -} - - -/* - * si470x_usb_driver_resume - resume the device - */ -static int si470x_usb_driver_resume(struct usb_interface *intf) -{ - struct si470x_device *radio = usb_get_intfdata(intf); - - printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); - - mutex_lock(&radio->lock); - if (radio->users && radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) - schedule_delayed_work(&radio->work, - msecs_to_jiffies(rds_poll_time)); - mutex_unlock(&radio->lock); - - return 0; -} - - -/* - * si470x_usb_driver_disconnect - disconnect the device - */ -static void si470x_usb_driver_disconnect(struct usb_interface *intf) -{ - struct si470x_device *radio = usb_get_intfdata(intf); - - mutex_lock(&radio->disconnect_lock); - radio->disconnected = 1; - cancel_delayed_work_sync(&radio->work); - usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - /* set led to disconnect state */ - si470x_set_led_state(radio, BLINK_ORANGE_LED); - - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - } - mutex_unlock(&radio->disconnect_lock); -} - - -/* - * si470x_usb_driver - usb driver interface - */ -static struct usb_driver si470x_usb_driver = { - .name = DRIVER_NAME, - .probe = si470x_usb_driver_probe, - .disconnect = si470x_usb_driver_disconnect, - .suspend = si470x_usb_driver_suspend, - .resume = si470x_usb_driver_resume, - .id_table = si470x_usb_driver_id_table, - .supports_autosuspend = 1, -}; - - - -/************************************************************************** - * Module Interface - **************************************************************************/ - -/* - * si470x_module_init - module init - */ -static int __init si470x_module_init(void) -{ - printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); - return usb_register(&si470x_usb_driver); -} - - -/* - * si470x_module_exit - module exit - */ -static void __exit si470x_module_exit(void) -{ - usb_deregister(&si470x_usb_driver); -} - - -module_init(si470x_module_init); -module_exit(si470x_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c new file mode 100644 index 00000000000..170bbe55478 --- /dev/null +++ b/drivers/media/radio/radio-si4713.c @@ -0,0 +1,366 @@ +/* + * drivers/media/radio/radio-si4713.c + * + * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter: + * + * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT + * Contact: Eduardo Valentin <eduardo.valentin@nokia.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/radio-si4713.h> + +/* module parameters */ +static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */ +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, + "Minor number for radio device (-1 ==> auto assign)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>"); +MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter"); +MODULE_VERSION("0.0.1"); + +/* Driver state struct */ +struct radio_si4713_device { + struct v4l2_device v4l2_dev; + struct video_device *radio_dev; +}; + +/* radio_si4713_fops - file operations interface */ +static const struct v4l2_file_operations radio_si4713_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, +}; + +/* Video4Linux Interface */ +static int radio_si4713_fill_audout(struct v4l2_audioout *vao) +{ + /* TODO: check presence of audio output */ + strlcpy(vao->name, "FM Modulator Audio Out", 32); + + return 0; +} + +static int radio_si4713_enumaudout(struct file *file, void *priv, + struct v4l2_audioout *vao) +{ + return radio_si4713_fill_audout(vao); +} + +static int radio_si4713_g_audout(struct file *file, void *priv, + struct v4l2_audioout *vao) +{ + int rval = radio_si4713_fill_audout(vao); + + vao->index = 0; + + return rval; +} + +static int radio_si4713_s_audout(struct file *file, void *priv, + struct v4l2_audioout *vao) +{ + return vao->index ? -EINVAL : 0; +} + +/* radio_si4713_querycap - query device capabilities */ +static int radio_si4713_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct radio_si4713_device *rsdev; + + rsdev = video_get_drvdata(video_devdata(file)); + + strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver)); + strlcpy(capability->card, "Silicon Labs Si4713 Modulator", + sizeof(capability->card)); + capability->capabilities = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; + + return 0; +} + +/* radio_si4713_queryctrl - enumerate control items */ +static int radio_si4713_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + /* Must be sorted from low to high control ID! */ + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_AUDIO_MUTE, + 0 + }; + + /* Must be sorted from low to high control ID! */ + static const u32 fmtx_ctrls[] = { + V4L2_CID_FM_TX_CLASS, + V4L2_CID_RDS_TX_DEVIATION, + V4L2_CID_RDS_TX_PI, + V4L2_CID_RDS_TX_PTY, + V4L2_CID_RDS_TX_PS_NAME, + V4L2_CID_RDS_TX_RADIO_TEXT, + V4L2_CID_AUDIO_LIMITER_ENABLED, + V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, + V4L2_CID_AUDIO_LIMITER_DEVIATION, + V4L2_CID_AUDIO_COMPRESSION_ENABLED, + V4L2_CID_AUDIO_COMPRESSION_GAIN, + V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, + V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, + V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, + V4L2_CID_PILOT_TONE_ENABLED, + V4L2_CID_PILOT_TONE_DEVIATION, + V4L2_CID_PILOT_TONE_FREQUENCY, + V4L2_CID_TUNE_PREEMPHASIS, + V4L2_CID_TUNE_POWER_LEVEL, + V4L2_CID_TUNE_ANTENNA_CAPACITOR, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + fmtx_ctrls, + NULL + }; + struct radio_si4713_device *rsdev; + + rsdev = video_get_drvdata(video_devdata(file)); + + qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); + if (qc->id == 0) + return -EINVAL; + + if (qc->id == V4L2_CID_USER_CLASS || qc->id == V4L2_CID_FM_TX_CLASS) + return v4l2_ctrl_query_fill(qc, 0, 0, 0, 0); + + return v4l2_device_call_until_err(&rsdev->v4l2_dev, 0, core, + queryctrl, qc); +} + +/* + * v4l2 ioctl call backs. + * we are just a wrapper for v4l2_sub_devs. + */ +static inline struct v4l2_device *get_v4l2_dev(struct file *file) +{ + return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev; +} + +static int radio_si4713_g_ext_ctrls(struct file *file, void *p, + struct v4l2_ext_controls *vecs) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + g_ext_ctrls, vecs); +} + +static int radio_si4713_s_ext_ctrls(struct file *file, void *p, + struct v4l2_ext_controls *vecs) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + s_ext_ctrls, vecs); +} + +static int radio_si4713_g_ctrl(struct file *file, void *p, + struct v4l2_control *vc) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + g_ctrl, vc); +} + +static int radio_si4713_s_ctrl(struct file *file, void *p, + struct v4l2_control *vc) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + s_ctrl, vc); +} + +static int radio_si4713_g_modulator(struct file *file, void *p, + struct v4l2_modulator *vm) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + g_modulator, vm); +} + +static int radio_si4713_s_modulator(struct file *file, void *p, + struct v4l2_modulator *vm) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + s_modulator, vm); +} + +static int radio_si4713_g_frequency(struct file *file, void *p, + struct v4l2_frequency *vf) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + g_frequency, vf); +} + +static int radio_si4713_s_frequency(struct file *file, void *p, + struct v4l2_frequency *vf) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner, + s_frequency, vf); +} + +static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg) +{ + return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, + ioctl, cmd, arg); +} + +static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = { + .vidioc_enumaudout = radio_si4713_enumaudout, + .vidioc_g_audout = radio_si4713_g_audout, + .vidioc_s_audout = radio_si4713_s_audout, + .vidioc_querycap = radio_si4713_querycap, + .vidioc_queryctrl = radio_si4713_queryctrl, + .vidioc_g_ext_ctrls = radio_si4713_g_ext_ctrls, + .vidioc_s_ext_ctrls = radio_si4713_s_ext_ctrls, + .vidioc_g_ctrl = radio_si4713_g_ctrl, + .vidioc_s_ctrl = radio_si4713_s_ctrl, + .vidioc_g_modulator = radio_si4713_g_modulator, + .vidioc_s_modulator = radio_si4713_s_modulator, + .vidioc_g_frequency = radio_si4713_g_frequency, + .vidioc_s_frequency = radio_si4713_s_frequency, + .vidioc_default = radio_si4713_default, +}; + +/* radio_si4713_vdev_template - video device interface */ +static struct video_device radio_si4713_vdev_template = { + .fops = &radio_si4713_fops, + .name = "radio-si4713", + .release = video_device_release, + .ioctl_ops = &radio_si4713_ioctl_ops, +}; + +/* Platform driver interface */ +/* radio_si4713_pdriver_probe - probe for the device */ +static int radio_si4713_pdriver_probe(struct platform_device *pdev) +{ + struct radio_si4713_platform_data *pdata = pdev->dev.platform_data; + struct radio_si4713_device *rsdev; + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; + int rval = 0; + + if (!pdata) { + dev_err(&pdev->dev, "Cannot proceed without platform data.\n"); + rval = -EINVAL; + goto exit; + } + + rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL); + if (!rsdev) { + dev_err(&pdev->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + + rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev); + if (rval) { + dev_err(&pdev->dev, "Failed to register v4l2 device.\n"); + goto free_rsdev; + } + + adapter = i2c_get_adapter(pdata->i2c_bus); + if (!adapter) { + dev_err(&pdev->dev, "Cannot get i2c adapter %d\n", + pdata->i2c_bus); + rval = -ENODEV; + goto unregister_v4l2_dev; + } + + sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, "si4713_i2c", + pdata->subdev_board_info, NULL); + if (!sd) { + dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); + rval = -ENODEV; + goto unregister_v4l2_dev; + } + + rsdev->radio_dev = video_device_alloc(); + if (!rsdev->radio_dev) { + dev_err(&pdev->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto unregister_v4l2_dev; + } + + memcpy(rsdev->radio_dev, &radio_si4713_vdev_template, + sizeof(radio_si4713_vdev_template)); + video_set_drvdata(rsdev->radio_dev, rsdev); + if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { + dev_err(&pdev->dev, "Could not register video device.\n"); + rval = -EIO; + goto free_vdev; + } + dev_info(&pdev->dev, "New device successfully probed\n"); + + goto exit; + +free_vdev: + video_device_release(rsdev->radio_dev); +unregister_v4l2_dev: + v4l2_device_unregister(&rsdev->v4l2_dev); +free_rsdev: + kfree(rsdev); +exit: + return rval; +} + +/* radio_si4713_pdriver_remove - remove the device */ +static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct radio_si4713_device *rsdev = container_of(v4l2_dev, + struct radio_si4713_device, + v4l2_dev); + + video_unregister_device(rsdev->radio_dev); + v4l2_device_unregister(&rsdev->v4l2_dev); + kfree(rsdev); + + return 0; +} + +static struct platform_driver radio_si4713_pdriver = { + .driver = { + .name = "radio-si4713", + }, + .probe = radio_si4713_pdriver_probe, + .remove = __exit_p(radio_si4713_pdriver_remove), +}; + +/* Module Interface */ +static int __init radio_si4713_module_init(void) +{ + return platform_driver_register(&radio_si4713_pdriver); +} + +static void __exit radio_si4713_module_exit(void) +{ + platform_driver_unregister(&radio_si4713_pdriver); +} + +module_init(radio_si4713_module_init); +module_exit(radio_si4713_module_exit); + diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig new file mode 100644 index 00000000000..a466654ee5c --- /dev/null +++ b/drivers/media/radio/si470x/Kconfig @@ -0,0 +1,37 @@ +config USB_SI470X + tristate "Silicon Labs Si470x FM Radio Receiver support with USB" + depends on USB && RADIO_SI470X + ---help--- + This is a driver for USB devices with the Silicon Labs SI470x + chip. Currently these devices are known to work: + - 10c4:818a: Silicon Labs USB FM Radio Reference Design + - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) + - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) + - 10c5:819a: Sanei Electric FM USB Radio (aka DealExtreme.com PCear) + + Sound is provided by the ALSA USB Audio/MIDI driver. Therefore + if you don't want to use the device solely for RDS receiving, + it is recommended to also select SND_USB_AUDIO. + + Please have a look at the documentation, especially on how + to redirect the audio stream from the radio to your sound device: + Documentation/video4linux/si470x.txt + + Say Y here if you want to connect this type of radio to your + computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called radio-usb-si470x. + +config I2C_SI470X + tristate "Silicon Labs Si470x FM Radio Receiver support with I2C" + depends on I2C && RADIO_SI470X && !USB_SI470X + ---help--- + This is a driver for I2C devices with the Silicon Labs SI470x + chip. + + Say Y here if you want to connect this type of radio to your + computer's I2C port. + + To compile this driver as a module, choose M here: the + module will be called radio-i2c-si470x. diff --git a/drivers/media/radio/si470x/Makefile b/drivers/media/radio/si470x/Makefile new file mode 100644 index 00000000000..06964816cfd --- /dev/null +++ b/drivers/media/radio/si470x/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for radios with Silicon Labs Si470x FM Radio Receivers +# + +radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o +radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o + +obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o +obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c new file mode 100644 index 00000000000..f33315f2c54 --- /dev/null +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -0,0 +1,798 @@ +/* + * drivers/media/radio/si470x/radio-si470x-common.c + * + * Driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * History: + * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.0 + * - First working version + * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.1 + * - Improved error handling, every function now returns errno + * - Improved multi user access (start/mute/stop) + * - Channel doesn't get lost anymore after start/mute/stop + * - RDS support added (polling mode via interrupt EP 1) + * - marked default module parameters with *value* + * - switched from bit structs to bit masks + * - header file cleaned and integrated + * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.2 + * - hex values are now lower case + * - commented USB ID for ADS/Tech moved on todo list + * - blacklisted si470x in hid-quirks.c + * - rds buffer handling functions integrated into *_work, *_read + * - rds_command in si470x_poll exchanged against simple retval + * - check for firmware version 15 + * - code order and prototypes still remain the same + * - spacing and bottom of band codes remain the same + * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.3 + * - code reordered to avoid function prototypes + * - switch/case defaults are now more user-friendly + * - unified comment style + * - applied all checkpatch.pl v1.12 suggestions + * except the warning about the too long lines with bit comments + * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) + * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.4 + * - avoid poss. locking when doing copy_to_user which may sleep + * - RDS is automatically activated on read now + * - code cleaned of unnecessary rds_commands + * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified + * (thanks to Guillaume RAMOUSSE) + * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.5 + * - number of seek_retries changed to tune_timeout + * - fixed problem with incomplete tune operations by own buffers + * - optimization of variables and printf types + * - improved error logging + * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net> + * Oliver Neukum <oliver@neukum.org> + * Version 1.0.6 + * - fixed coverity checker warnings in *_usb_driver_disconnect + * - probe()/open() race by correct ordering in probe() + * - DMA coherency rules by separate allocation of all buffers + * - use of endianness macros + * - abuse of spinlock, replaced by mutex + * - racy handling of timer in disconnect, + * replaced by delayed_work + * - racy interruptible_sleep_on(), + * replaced with wait_event_interruptible() + * - handle signals in read() + * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net> + * Oliver Neukum <oliver@neukum.org> + * Version 1.0.7 + * - usb autosuspend support + * - unplugging fixed + * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.8 + * - hardware frequency seek support + * - afc indication + * - more safety checks, let si470x_get_freq return errno + * - vidioc behavior corrected according to v4l2 spec + * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com> + * - add support for KWorld USB FM Radio FM700 + * - blacklisted KWorld radio in hid-core.c and hid-ids.h + * 2008-12-03 Mark Lord <mlord@pobox.com> + * - add support for DealExtreme USB Radio + * 2009-01-31 Bob Ross <pigiron@gmx.com> + * - correction of stereo detection/setting + * - correction of signal strength indicator scaling + * 2009-01-31 Rick Bronson <rick@efn.org> + * Tobias Lorenz <tobias.lorenz@gmx.net> + * - add LED status output + * - get HW/SW version from scratchpad + * 2009-06-16 Edouard Lafargue <edouard@lafargue.name> + * Version 1.0.10 + * - add support for interrupt mode for RDS endpoint, + * instead of polling. + * Improves RDS reception significantly + */ + + +/* kernel includes */ +#include "radio-si470x.h" + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Spacing (kHz) */ +/* 0: 200 kHz (USA, Australia) */ +/* 1: 100 kHz (Europe, Japan) */ +/* 2: 50 kHz */ +static unsigned short space = 2; +module_param(space, ushort, 0444); +MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); + +/* Bottom of Band (MHz) */ +/* 0: 87.5 - 108 MHz (USA, Europe)*/ +/* 1: 76 - 108 MHz (Japan wide band) */ +/* 2: 76 - 90 MHz (Japan) */ +static unsigned short band = 1; +module_param(band, ushort, 0444); +MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); + +/* De-emphasis */ +/* 0: 75 us (USA) */ +/* 1: 50 us (Europe, Australia, Japan) */ +static unsigned short de = 1; +module_param(de, ushort, 0444); +MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*"); + +/* Tune timeout */ +static unsigned int tune_timeout = 3000; +module_param(tune_timeout, uint, 0644); +MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); + +/* Seek timeout */ +static unsigned int seek_timeout = 5000; +module_param(seek_timeout, uint, 0644); +MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); + + + +/************************************************************************** + * Generic Functions + **************************************************************************/ + +/* + * si470x_set_chan - set the channel + */ +static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) +{ + int retval; + unsigned long timeout; + bool timed_out = 0; + + /* start tuning */ + radio->registers[CHANNEL] &= ~CHANNEL_CHAN; + radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; + retval = si470x_set_register(radio, CHANNEL); + if (retval < 0) + goto done; + + /* wait till tune operation has completed */ + timeout = jiffies + msecs_to_jiffies(tune_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + dev_warn(&radio->videodev->dev, "tune does not complete\n"); + if (timed_out) + dev_warn(&radio->videodev->dev, + "tune timed out after %u ms\n", tune_timeout); + +stop: + /* stop tuning */ + radio->registers[CHANNEL] &= ~CHANNEL_TUNE; + retval = si470x_set_register(radio, CHANNEL); + +done: + return retval; +} + + +/* + * si470x_get_freq - get the frequency + */ +static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) +{ + unsigned int spacing, band_bottom; + unsigned short chan; + int retval; + + /* Spacing (kHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { + /* 0: 200 kHz (USA, Australia) */ + case 0: + spacing = 0.200 * FREQ_MUL; break; + /* 1: 100 kHz (Europe, Japan) */ + case 1: + spacing = 0.100 * FREQ_MUL; break; + /* 2: 50 kHz */ + default: + spacing = 0.050 * FREQ_MUL; break; + }; + + /* Bottom of Band (MHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe) */ + case 0: + band_bottom = 87.5 * FREQ_MUL; break; + /* 1: 76 - 108 MHz (Japan wide band) */ + default: + band_bottom = 76 * FREQ_MUL; break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + band_bottom = 76 * FREQ_MUL; break; + }; + + /* read channel */ + retval = si470x_get_register(radio, READCHAN); + chan = radio->registers[READCHAN] & READCHAN_READCHAN; + + /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ + *freq = chan * spacing + band_bottom; + + return retval; +} + + +/* + * si470x_set_freq - set the frequency + */ +int si470x_set_freq(struct si470x_device *radio, unsigned int freq) +{ + unsigned int spacing, band_bottom; + unsigned short chan; + + /* Spacing (kHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { + /* 0: 200 kHz (USA, Australia) */ + case 0: + spacing = 0.200 * FREQ_MUL; break; + /* 1: 100 kHz (Europe, Japan) */ + case 1: + spacing = 0.100 * FREQ_MUL; break; + /* 2: 50 kHz */ + default: + spacing = 0.050 * FREQ_MUL; break; + }; + + /* Bottom of Band (MHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe) */ + case 0: + band_bottom = 87.5 * FREQ_MUL; break; + /* 1: 76 - 108 MHz (Japan wide band) */ + default: + band_bottom = 76 * FREQ_MUL; break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + band_bottom = 76 * FREQ_MUL; break; + }; + + /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ + chan = (freq - band_bottom) / spacing; + + return si470x_set_chan(radio, chan); +} + + +/* + * si470x_set_seek - set seek + */ +static int si470x_set_seek(struct si470x_device *radio, + unsigned int wrap_around, unsigned int seek_upward) +{ + int retval = 0; + unsigned long timeout; + bool timed_out = 0; + + /* start seeking */ + radio->registers[POWERCFG] |= POWERCFG_SEEK; + if (wrap_around == 1) + radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; + else + radio->registers[POWERCFG] |= POWERCFG_SKMODE; + if (seek_upward == 1) + radio->registers[POWERCFG] |= POWERCFG_SEEKUP; + else + radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* wait till seek operation has completed */ + timeout = jiffies + msecs_to_jiffies(seek_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + dev_warn(&radio->videodev->dev, "seek does not complete\n"); + if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) + dev_warn(&radio->videodev->dev, + "seek failed / band limit reached\n"); + if (timed_out) + dev_warn(&radio->videodev->dev, + "seek timed out after %u ms\n", seek_timeout); + +stop: + /* stop seeking */ + radio->registers[POWERCFG] &= ~POWERCFG_SEEK; + retval = si470x_set_register(radio, POWERCFG); + +done: + /* try again, if timed out */ + if ((retval == 0) && timed_out) + retval = -EAGAIN; + + return retval; +} + + +/* + * si470x_start - switch on radio + */ +int si470x_start(struct si470x_device *radio) +{ + int retval; + + /* powercfg */ + radio->registers[POWERCFG] = + POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* sysconfig 1 */ + radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + goto done; + + /* sysconfig 2 */ + radio->registers[SYSCONFIG2] = + (0x3f << 8) | /* SEEKTH */ + ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ + ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ + 15; /* VOLUME (max) */ + retval = si470x_set_register(radio, SYSCONFIG2); + if (retval < 0) + goto done; + + /* reset last channel */ + retval = si470x_set_chan(radio, + radio->registers[CHANNEL] & CHANNEL_CHAN); + +done: + return retval; +} + + +/* + * si470x_stop - switch off radio + */ +int si470x_stop(struct si470x_device *radio) +{ + int retval; + + /* sysconfig 1 */ + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + goto done; + + /* powercfg */ + radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; + /* POWERCFG_ENABLE has to automatically go low */ + radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; + retval = si470x_set_register(radio, POWERCFG); + +done: + return retval; +} + + +/* + * si470x_rds_on - switch on rds reception + */ +int si470x_rds_on(struct si470x_device *radio) +{ + int retval; + + /* sysconfig 1 */ + mutex_lock(&radio->lock); + radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; + mutex_unlock(&radio->lock); + + return retval; +} + + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_queryctrl - enumerate control items + */ +static int si470x_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = -EINVAL; + + /* abort if qc->id is below V4L2_CID_BASE */ + if (qc->id < V4L2_CID_BASE) + goto done; + + /* search video control */ + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + } + + /* disable unsupported base controls */ + /* to satisfy kradio and such apps */ + if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { + qc->flags = V4L2_CTRL_FLAG_DISABLED; + retval = 0; + } + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "query controls failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_ctrl - get the value of a control + */ +static int si470x_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = radio->registers[SYSCONFIG2] & + SYSCONFIG2_VOLUME; + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = ((radio->registers[POWERCFG] & + POWERCFG_DMUTE) == 0) ? 1 : 0; + break; + default: + retval = -EINVAL; + } + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "get control failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_ctrl - set the value of a control + */ +static int si470x_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; + radio->registers[SYSCONFIG2] |= ctrl->value; + retval = si470x_set_register(radio, SYSCONFIG2); + break; + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value == 1) + radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; + else + radio->registers[POWERCFG] |= POWERCFG_DMUTE; + retval = si470x_set_register(radio, POWERCFG); + break; + default: + retval = -EINVAL; + } + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "set control failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_audio - get audio attributes + */ +static int si470x_vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + /* driver constants */ + audio->index = 0; + strcpy(audio->name, "Radio"); + audio->capability = V4L2_AUDCAP_STEREO; + audio->mode = 0; + + return 0; +} + + +/* + * si470x_vidioc_g_tuner - get tuner attributes + */ +static int si470x_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + if (tuner->index != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto done; + + /* driver constants */ + strcpy(tuner->name, "FM"); + tuner->type = V4L2_TUNER_RADIO; +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) + tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_RDS; +#else + tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; +#endif + + /* range limits */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe, default) */ + default: + tuner->rangelow = 87.5 * FREQ_MUL; + tuner->rangehigh = 108 * FREQ_MUL; + break; + /* 1: 76 - 108 MHz (Japan wide band) */ + case 1: + tuner->rangelow = 76 * FREQ_MUL; + tuner->rangehigh = 108 * FREQ_MUL; + break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + tuner->rangelow = 76 * FREQ_MUL; + tuner->rangehigh = 90 * FREQ_MUL; + break; + }; + + /* stereo indicator == stereo (instead of mono) */ + if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) + tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) + /* If there is a reliable method of detecting an RDS channel, + then this code should check for that before setting this + RDS subchannel. */ + tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; +#endif + + /* mono/stereo selector */ + if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) + tuner->audmode = V4L2_TUNER_MODE_STEREO; + else + tuner->audmode = V4L2_TUNER_MODE_MONO; + + /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ + /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */ + tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); + /* the ideal factor is 0xffff/75 = 873,8 */ + tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); + + /* automatic frequency control: -1: freq to low, 1 freq to high */ + /* AFCRL does only indicate that freq. differs, not if too low/high */ + tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "get tuner failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_tuner - set tuner attributes + */ +static int si470x_vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = -EINVAL; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + if (tuner->index != 0) + goto done; + + /* mono/stereo selector */ + switch (tuner->audmode) { + case V4L2_TUNER_MODE_MONO: + radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ + break; + case V4L2_TUNER_MODE_STEREO: + radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ + break; + default: + goto done; + } + + retval = si470x_set_register(radio, POWERCFG); + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "set tuner failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_frequency - get tuner or modulator radio frequency + */ +static int si470x_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + if (freq->tuner != 0) { + retval = -EINVAL; + goto done; + } + + freq->type = V4L2_TUNER_RADIO; + retval = si470x_get_freq(radio, &freq->frequency); + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "get frequency failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_frequency - set tuner or modulator radio frequency + */ +static int si470x_vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + if (freq->tuner != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_freq(radio, freq->frequency); + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "set frequency failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek + */ +static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + retval = si470x_disconnect_check(radio); + if (retval) + goto done; + + if (seek->tuner != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); + +done: + if (retval < 0) + dev_warn(&radio->videodev->dev, + "set hardware frequency seek failed with %d\n", retval); + return retval; +} + + +/* + * si470x_ioctl_ops - video device ioctl operations + */ +static const struct v4l2_ioctl_ops si470x_ioctl_ops = { + .vidioc_querycap = si470x_vidioc_querycap, + .vidioc_queryctrl = si470x_vidioc_queryctrl, + .vidioc_g_ctrl = si470x_vidioc_g_ctrl, + .vidioc_s_ctrl = si470x_vidioc_s_ctrl, + .vidioc_g_audio = si470x_vidioc_g_audio, + .vidioc_g_tuner = si470x_vidioc_g_tuner, + .vidioc_s_tuner = si470x_vidioc_s_tuner, + .vidioc_g_frequency = si470x_vidioc_g_frequency, + .vidioc_s_frequency = si470x_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, +}; + + +/* + * si470x_viddev_template - video device interface + */ +struct video_device si470x_viddev_template = { + .fops = &si470x_fops, + .name = DRIVER_NAME, + .release = video_device_release, + .ioctl_ops = &si470x_ioctl_ops, +}; diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c new file mode 100644 index 00000000000..2d53b6a9409 --- /dev/null +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -0,0 +1,401 @@ +/* + * drivers/media/radio/si470x/radio-si470x-i2c.c + * + * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * ToDo: + * - RDS support + */ + + +/* driver definitions */ +#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>"; +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0) +#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" +#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers" +#define DRIVER_VERSION "1.0.0" + +/* kernel includes */ +#include <linux/i2c.h> +#include <linux/delay.h> + +#include "radio-si470x.h" + + +/* I2C Device ID List */ +static const struct i2c_device_id si470x_i2c_id[] = { + /* Generic Entry */ + { "si470x", 0 }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Radio Nr */ +static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + + + +/************************************************************************** + * I2C Definitions + **************************************************************************/ + +/* Write starts with the upper byte of register 0x02 */ +#define WRITE_REG_NUM 8 +#define WRITE_INDEX(i) (i + 0x02) + +/* Read starts with the upper byte of register 0x0a */ +#define READ_REG_NUM RADIO_REGISTER_NUM +#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM) + + + +/************************************************************************** + * General Driver Functions - REGISTERs + **************************************************************************/ + +/* + * si470x_get_register - read register + */ +int si470x_get_register(struct si470x_device *radio, int regnr) +{ + u16 buf[READ_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, + (void *)buf }, + }; + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]); + + return 0; +} + + +/* + * si470x_set_register - write register + */ +int si470x_set_register(struct si470x_device *radio, int regnr) +{ + int i; + u16 buf[WRITE_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM, + (void *)buf }, + }; + + for (i = 0; i < WRITE_REG_NUM; i++) + buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]); + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + return 0; +} + + + +/************************************************************************** + * General Driver Functions - ENTIRE REGISTERS + **************************************************************************/ + +/* + * si470x_get_all_registers - read entire registers + */ +static int si470x_get_all_registers(struct si470x_device *radio) +{ + int i; + u16 buf[READ_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, + (void *)buf }, + }; + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + for (i = 0; i < READ_REG_NUM; i++) + radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]); + + return 0; +} + + + +/************************************************************************** + * General Driver Functions - DISCONNECT_CHECK + **************************************************************************/ + +/* + * si470x_disconnect_check - check whether radio disconnects + */ +int si470x_disconnect_check(struct si470x_device *radio) +{ + return 0; +} + + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si470x_fops_open - file open + */ +static int si470x_fops_open(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + mutex_lock(&radio->lock); + radio->users++; + + if (radio->users == 1) + /* start radio */ + retval = si470x_start(radio); + + mutex_unlock(&radio->lock); + + return retval; +} + + +/* + * si470x_fops_release - file release + */ +static int si470x_fops_release(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) + return -ENODEV; + + mutex_lock(&radio->lock); + radio->users--; + if (radio->users == 0) + /* stop radio */ + retval = si470x_stop(radio); + + mutex_unlock(&radio->lock); + + return retval; +} + + +/* + * si470x_fops - file operations interface + */ +const struct v4l2_file_operations si470x_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .open = si470x_fops_open, + .release = si470x_fops_release, +}; + + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_querycap - query device capabilities + */ +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + capability->version = DRIVER_KERNEL_VERSION; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO; + + return 0; +} + + + +/************************************************************************** + * I2C Interface + **************************************************************************/ + +/* + * si470x_i2c_probe - probe for the device + */ +static int __devinit si470x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si470x_device *radio; + int retval = 0; + unsigned char version_warning = 0; + + /* private data allocation and initialization */ + radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + if (!radio) { + retval = -ENOMEM; + goto err_initial; + } + radio->users = 0; + radio->client = client; + mutex_init(&radio->lock); + + /* video device allocation and initialization */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + retval = -ENOMEM; + goto err_radio; + } + memcpy(radio->videodev, &si470x_viddev_template, + sizeof(si470x_viddev_template)); + video_set_drvdata(radio->videodev, radio); + + /* power up : need 110ms */ + radio->registers[POWERCFG] = POWERCFG_ENABLE; + if (si470x_set_register(radio, POWERCFG) < 0) { + retval = -EIO; + goto err_all; + } + msleep(110); + + /* get device and chip versions */ + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; + goto err_video; + } + dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", + radio->registers[DEVICEID], radio->registers[CHIPID]); + if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + dev_warn(&client->dev, + "This driver is known to work with " + "firmware version %hu,\n", RADIO_FW_VERSION); + dev_warn(&client->dev, + "but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); + version_warning = 1; + } + + /* give out version warning */ + if (version_warning == 1) { + dev_warn(&client->dev, + "If you have some trouble using this driver,\n"); + dev_warn(&client->dev, + "please report to V4L ML at " + "linux-media@vger.kernel.org\n"); + } + + /* set initial frequency */ + si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + + /* register video device */ + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr); + if (retval) { + dev_warn(&client->dev, "Could not register video device\n"); + goto err_all; + } + i2c_set_clientdata(client, radio); + + return 0; +err_all: +err_video: + video_device_release(radio->videodev); +err_radio: + kfree(radio); +err_initial: + return retval; +} + + +/* + * si470x_i2c_remove - remove the device + */ +static __devexit int si470x_i2c_remove(struct i2c_client *client) +{ + struct si470x_device *radio = i2c_get_clientdata(client); + + video_unregister_device(radio->videodev); + kfree(radio); + i2c_set_clientdata(client, NULL); + + return 0; +} + + +/* + * si470x_i2c_driver - i2c driver interface + */ +static struct i2c_driver si470x_i2c_driver = { + .driver = { + .name = "si470x", + .owner = THIS_MODULE, + }, + .probe = si470x_i2c_probe, + .remove = __devexit_p(si470x_i2c_remove), + .id_table = si470x_i2c_id, +}; + + + +/************************************************************************** + * Module Interface + **************************************************************************/ + +/* + * si470x_i2c_init - module init + */ +static int __init si470x_i2c_init(void) +{ + printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); + return i2c_add_driver(&si470x_i2c_driver); +} + + +/* + * si470x_i2c_exit - module exit + */ +static void __exit si470x_i2c_exit(void) +{ + i2c_del_driver(&si470x_i2c_driver); +} + + +module_init(si470x_i2c_init); +module_exit(si470x_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c new file mode 100644 index 00000000000..f2d0e1ddb30 --- /dev/null +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -0,0 +1,988 @@ +/* + * drivers/media/radio/si470x/radio-si470x-usb.c + * + * USB driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * ToDo: + * - add firmware download/update support + */ + + +/* driver definitions */ +#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10) +#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" +#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" +#define DRIVER_VERSION "1.0.10" + +/* kernel includes */ +#include <linux/usb.h> +#include <linux/hid.h> + +#include "radio-si470x.h" + + +/* USB Device ID List */ +static struct usb_device_id si470x_usb_driver_id_table[] = { + /* Silicon Labs USB FM Radio Reference Design */ + { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, + /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, + /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, + /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Radio Nr */ +static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + +/* USB timeout */ +static unsigned int usb_timeout = 500; +module_param(usb_timeout, uint, 0644); +MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); + +/* RDS buffer blocks */ +static unsigned int rds_buf = 100; +module_param(rds_buf, uint, 0444); +MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); + +/* RDS maximum block errors */ +static unsigned short max_rds_errors = 1; +/* 0 means 0 errors requiring correction */ +/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ +/* 2 means 3-5 errors requiring correction */ +/* 3 means 6+ errors or errors in checkword, correction not possible */ +module_param(max_rds_errors, ushort, 0644); +MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); + + + +/************************************************************************** + * USB HID Reports + **************************************************************************/ + +/* Reports 1-16 give direct read/write access to the 16 Si470x registers */ +/* with the (REPORT_ID - 1) corresponding to the register address across USB */ +/* endpoint 0 using GET_REPORT and SET_REPORT */ +#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) +#define REGISTER_REPORT(reg) ((reg) + 1) + +/* Report 17 gives direct read/write access to the entire Si470x register */ +/* map across endpoint 0 using GET_REPORT and SET_REPORT */ +#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) +#define ENTIRE_REPORT 17 + +/* Report 18 is used to send the lowest 6 Si470x registers up the HID */ +/* interrupt endpoint 1 to Windows every 20 milliseconds for status */ +#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) +#define RDS_REPORT 18 + +/* Report 19: LED state */ +#define LED_REPORT_SIZE 3 +#define LED_REPORT 19 + +/* Report 19: stream */ +#define STREAM_REPORT_SIZE 3 +#define STREAM_REPORT 19 + +/* Report 20: scratch */ +#define SCRATCH_PAGE_SIZE 63 +#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) +#define SCRATCH_REPORT 20 + +/* Reports 19-22: flash upgrade of the C8051F321 */ +#define WRITE_REPORT_SIZE 4 +#define WRITE_REPORT 19 +#define FLASH_REPORT_SIZE 64 +#define FLASH_REPORT 20 +#define CRC_REPORT_SIZE 3 +#define CRC_REPORT 21 +#define RESPONSE_REPORT_SIZE 2 +#define RESPONSE_REPORT 22 + +/* Report 23: currently unused, but can accept 60 byte reports on the HID */ +/* interrupt out endpoint 2 every 1 millisecond */ +#define UNUSED_REPORT 23 + + + +/************************************************************************** + * Software/Hardware Versions from Scratch Page + **************************************************************************/ +#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 +#define RADIO_SW_VERSION 7 +#define RADIO_HW_VERSION 1 + + + +/************************************************************************** + * LED State Definitions + **************************************************************************/ +#define LED_COMMAND 0x35 + +#define NO_CHANGE_LED 0x00 +#define ALL_COLOR_LED 0x01 /* streaming state */ +#define BLINK_GREEN_LED 0x02 /* connect state */ +#define BLINK_RED_LED 0x04 +#define BLINK_ORANGE_LED 0x10 /* disconnect state */ +#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ +#define SOLID_RED_LED 0x40 /* bootload state */ +#define SOLID_ORANGE_LED 0x80 + + + +/************************************************************************** + * Stream State Definitions + **************************************************************************/ +#define STREAM_COMMAND 0x36 +#define STREAM_VIDPID 0x00 +#define STREAM_AUDIO 0xff + + + +/************************************************************************** + * Bootloader / Flash Commands + **************************************************************************/ + +/* unique id sent to bootloader and required to put into a bootload state */ +#define UNIQUE_BL_ID 0x34 + +/* mask for the flash data */ +#define FLASH_DATA_MASK 0x55 + +/* bootloader commands */ +#define GET_SW_VERSION_COMMAND 0x00 +#define SET_PAGE_COMMAND 0x01 +#define ERASE_PAGE_COMMAND 0x02 +#define WRITE_PAGE_COMMAND 0x03 +#define CRC_ON_PAGE_COMMAND 0x04 +#define READ_FLASH_BYTE_COMMAND 0x05 +#define RESET_DEVICE_COMMAND 0x06 +#define GET_HW_VERSION_COMMAND 0x07 +#define BLANK 0xff + +/* bootloader command responses */ +#define COMMAND_OK 0x01 +#define COMMAND_FAILED 0x02 +#define COMMAND_PENDING 0x03 + + + +/************************************************************************** + * General Driver Functions - REGISTER_REPORTs + **************************************************************************/ + +/* + * si470x_get_report - receive a HID report + */ +static int si470x_get_report(struct si470x_device *radio, void *buf, int size) +{ + unsigned char *report = (unsigned char *) buf; + int retval; + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + HID_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + report[0], 2, + buf, size, usb_timeout); + + if (retval < 0) + dev_warn(&radio->intf->dev, + "si470x_get_report: usb_control_msg returned %d\n", + retval); + return retval; +} + + +/* + * si470x_set_report - send a HID report + */ +static int si470x_set_report(struct si470x_device *radio, void *buf, int size) +{ + unsigned char *report = (unsigned char *) buf; + int retval; + + retval = usb_control_msg(radio->usbdev, + usb_sndctrlpipe(radio->usbdev, 0), + HID_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + report[0], 2, + buf, size, usb_timeout); + + if (retval < 0) + dev_warn(&radio->intf->dev, + "si470x_set_report: usb_control_msg returned %d\n", + retval); + return retval; +} + + +/* + * si470x_get_register - read register + */ +int si470x_get_register(struct si470x_device *radio, int regnr) +{ + unsigned char buf[REGISTER_REPORT_SIZE]; + int retval; + + buf[0] = REGISTER_REPORT(regnr); + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval >= 0) + radio->registers[regnr] = get_unaligned_be16(&buf[1]); + + return (retval < 0) ? -EINVAL : 0; +} + + +/* + * si470x_set_register - write register + */ +int si470x_set_register(struct si470x_device *radio, int regnr) +{ + unsigned char buf[REGISTER_REPORT_SIZE]; + int retval; + + buf[0] = REGISTER_REPORT(regnr); + put_unaligned_be16(radio->registers[regnr], &buf[1]); + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - ENTIRE_REPORT + **************************************************************************/ + +/* + * si470x_get_all_registers - read entire registers + */ +static int si470x_get_all_registers(struct si470x_device *radio) +{ + unsigned char buf[ENTIRE_REPORT_SIZE]; + int retval; + unsigned char regnr; + + buf[0] = ENTIRE_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval >= 0) + for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - LED_REPORT + **************************************************************************/ + +/* + * si470x_set_led_state - sets the led state + */ +static int si470x_set_led_state(struct si470x_device *radio, + unsigned char led_state) +{ + unsigned char buf[LED_REPORT_SIZE]; + int retval; + + buf[0] = LED_REPORT; + buf[1] = LED_COMMAND; + buf[2] = led_state; + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - SCRATCH_REPORT + **************************************************************************/ + +/* + * si470x_get_scratch_versions - gets the scratch page and version infos + */ +static int si470x_get_scratch_page_versions(struct si470x_device *radio) +{ + unsigned char buf[SCRATCH_REPORT_SIZE]; + int retval; + + buf[0] = SCRATCH_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval < 0) + dev_warn(&radio->intf->dev, "si470x_get_scratch: " + "si470x_get_report returned %d\n", retval); + else { + radio->software_version = buf[1]; + radio->hardware_version = buf[2]; + } + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - DISCONNECT_CHECK + **************************************************************************/ + +/* + * si470x_disconnect_check - check whether radio disconnects + */ +int si470x_disconnect_check(struct si470x_device *radio) +{ + if (radio->disconnected) + return -EIO; + else + return 0; +} + + + +/************************************************************************** + * RDS Driver Functions + **************************************************************************/ + +/* + * si470x_int_in_callback - rds callback and processing function + * + * TODO: do we need to use mutex locks in some sections? + */ +static void si470x_int_in_callback(struct urb *urb) +{ + struct si470x_device *radio = urb->context; + unsigned char buf[RDS_REPORT_SIZE]; + int retval; + unsigned char regnr; + unsigned char blocknum; + unsigned short bler; /* rds block errors */ + unsigned short rds; + unsigned char tmpbuf[3]; + + if (urb->status) { + if (urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { + return; + } else { + dev_warn(&radio->intf->dev, + "non-zero urb status (%d)\n", urb->status); + goto resubmit; /* Maybe we can recover. */ + } + } + + /* safety checks */ + if (radio->disconnected) + return; + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + goto resubmit; + + if (urb->actual_length > 0) { + /* Update RDS registers with URB data */ + buf[0] = RDS_REPORT; + for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) + radio->registers[STATUSRSSI + regnr] = + get_unaligned_be16(&radio->int_in_buffer[ + regnr * RADIO_REGISTER_SIZE + 1]); + /* get rds blocks */ + if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { + /* No RDS group ready, better luck next time */ + goto resubmit; + } + if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { + /* RDS decoder not synchronized */ + goto resubmit; + } + for (blocknum = 0; blocknum < 4; blocknum++) { + switch (blocknum) { + default: + bler = (radio->registers[STATUSRSSI] & + STATUSRSSI_BLERA) >> 9; + rds = radio->registers[RDSA]; + break; + case 1: + bler = (radio->registers[READCHAN] & + READCHAN_BLERB) >> 14; + rds = radio->registers[RDSB]; + break; + case 2: + bler = (radio->registers[READCHAN] & + READCHAN_BLERC) >> 12; + rds = radio->registers[RDSC]; + break; + case 3: + bler = (radio->registers[READCHAN] & + READCHAN_BLERD) >> 10; + rds = radio->registers[RDSD]; + break; + }; + + /* Fill the V4L2 RDS buffer */ + put_unaligned_le16(rds, &tmpbuf); + tmpbuf[2] = blocknum; /* offset name */ + tmpbuf[2] |= blocknum << 3; /* received offset */ + if (bler > max_rds_errors) + tmpbuf[2] |= 0x80; /* uncorrectable errors */ + else if (bler > 0) + tmpbuf[2] |= 0x40; /* corrected error(s) */ + + /* copy RDS block to internal buffer */ + memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); + radio->wr_index += 3; + + /* wrap write pointer */ + if (radio->wr_index >= radio->buf_size) + radio->wr_index = 0; + + /* check for overflow */ + if (radio->wr_index == radio->rd_index) { + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; + } + } + if (radio->wr_index != radio->rd_index) + wake_up_interruptible(&radio->read_queue); + } + +resubmit: + /* Resubmit if we're still running. */ + if (radio->int_in_running && radio->usbdev) { + retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); + if (retval) { + dev_warn(&radio->intf->dev, + "resubmitting urb failed (%d)", retval); + radio->int_in_running = 0; + } + } +} + + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si470x_fops_read - read RDS data + */ +static ssize_t si470x_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + unsigned int block_count = 0; + + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); + + /* block if no new data available */ + while (radio->wr_index == radio->rd_index) { + if (file->f_flags & O_NONBLOCK) { + retval = -EWOULDBLOCK; + goto done; + } + if (wait_event_interruptible(radio->read_queue, + radio->wr_index != radio->rd_index) < 0) { + retval = -EINTR; + goto done; + } + } + + /* calculate block count from byte count */ + count /= 3; + + /* copy RDS block out of internal buffer and to user buffer */ + mutex_lock(&radio->lock); + while (block_count < count) { + if (radio->rd_index == radio->wr_index) + break; + + /* always transfer rds complete blocks */ + if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) + /* retval = -EFAULT; */ + break; + + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; + + /* increment counters */ + block_count++; + buf += 3; + retval += 3; + } + mutex_unlock(&radio->lock); + +done: + return retval; +} + + +/* + * si470x_fops_poll - poll RDS data + */ +static unsigned int si470x_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); + + poll_wait(file, &radio->read_queue, pts); + + if (radio->rd_index != radio->wr_index) + retval = POLLIN | POLLRDNORM; + + return retval; +} + + +/* + * si470x_fops_open - file open + */ +static int si470x_fops_open(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval; + + lock_kernel(); + radio->users++; + + retval = usb_autopm_get_interface(radio->intf); + if (retval < 0) { + radio->users--; + retval = -EIO; + goto done; + } + + if (radio->users == 1) { + /* start radio */ + retval = si470x_start(radio); + if (retval < 0) { + usb_autopm_put_interface(radio->intf); + goto done; + } + + /* initialize interrupt urb */ + usb_fill_int_urb(radio->int_in_urb, radio->usbdev, + usb_rcvintpipe(radio->usbdev, + radio->int_in_endpoint->bEndpointAddress), + radio->int_in_buffer, + le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), + si470x_int_in_callback, + radio, + radio->int_in_endpoint->bInterval); + + radio->int_in_running = 1; + mb(); + + retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); + if (retval) { + dev_info(&radio->intf->dev, + "submitting int urb failed (%d)\n", retval); + radio->int_in_running = 0; + usb_autopm_put_interface(radio->intf); + } + } + +done: + unlock_kernel(); + return retval; +} + + +/* + * si470x_fops_release - file release + */ +static int si470x_fops_release(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) { + retval = -ENODEV; + goto done; + } + + mutex_lock(&radio->disconnect_lock); + radio->users--; + if (radio->users == 0) { + /* shutdown interrupt handler */ + if (radio->int_in_running) { + radio->int_in_running = 0; + if (radio->int_in_urb) + usb_kill_urb(radio->int_in_urb); + } + + if (radio->disconnected) { + video_unregister_device(radio->videodev); + kfree(radio->int_in_buffer); + kfree(radio->buffer); + kfree(radio); + goto unlock; + } + + /* cancel read processes */ + wake_up_interruptible(&radio->read_queue); + + /* stop radio */ + retval = si470x_stop(radio); + usb_autopm_put_interface(radio->intf); + } +unlock: + mutex_unlock(&radio->disconnect_lock); +done: + return retval; +} + + +/* + * si470x_fops - file operations interface + */ +const struct v4l2_file_operations si470x_fops = { + .owner = THIS_MODULE, + .read = si470x_fops_read, + .poll = si470x_fops_poll, + .ioctl = video_ioctl2, + .open = si470x_fops_open, + .release = si470x_fops_release, +}; + + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_querycap - query device capabilities + */ +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si470x_device *radio = video_drvdata(file); + + strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + usb_make_path(radio->usbdev, capability->bus_info, + sizeof(capability->bus_info)); + capability->version = DRIVER_KERNEL_VERSION; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; + + return 0; +} + + + +/************************************************************************** + * USB Interface + **************************************************************************/ + +/* + * si470x_usb_driver_probe - probe for the device + */ +static int si470x_usb_driver_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct si470x_device *radio; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i, int_end_size, retval = 0; + unsigned char version_warning = 0; + + /* private data allocation and initialization */ + radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + if (!radio) { + retval = -ENOMEM; + goto err_initial; + } + radio->users = 0; + radio->disconnected = 0; + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + mutex_init(&radio->disconnect_lock); + mutex_init(&radio->lock); + + iface_desc = intf->cur_altsetting; + + /* Set up interrupt endpoint information. */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_IN) && ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) + radio->int_in_endpoint = endpoint; + } + if (!radio->int_in_endpoint) { + dev_info(&intf->dev, "could not find interrupt in endpoint\n"); + retval = -EIO; + goto err_radio; + } + + int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); + + radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); + if (!radio->int_in_buffer) { + dev_info(&intf->dev, "could not allocate int_in_buffer"); + retval = -ENOMEM; + goto err_radio; + } + + radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!radio->int_in_urb) { + dev_info(&intf->dev, "could not allocate int_in_urb"); + retval = -ENOMEM; + goto err_intbuffer; + } + + /* video device allocation and initialization */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + retval = -ENOMEM; + goto err_intbuffer; + } + memcpy(radio->videodev, &si470x_viddev_template, + sizeof(si470x_viddev_template)); + video_set_drvdata(radio->videodev, radio); + + /* get device and chip versions */ + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; + goto err_video; + } + dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", + radio->registers[DEVICEID], radio->registers[CHIPID]); + if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + dev_warn(&intf->dev, + "This driver is known to work with " + "firmware version %hu,\n", RADIO_FW_VERSION); + dev_warn(&intf->dev, + "but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); + version_warning = 1; + } + + /* get software and hardware versions */ + if (si470x_get_scratch_page_versions(radio) < 0) { + retval = -EIO; + goto err_video; + } + dev_info(&intf->dev, "software version %d, hardware version %d\n", + radio->software_version, radio->hardware_version); + if (radio->software_version < RADIO_SW_VERSION) { + dev_warn(&intf->dev, + "This driver is known to work with " + "software version %hu,\n", RADIO_SW_VERSION); + dev_warn(&intf->dev, + "but the device has software version %hu.\n", + radio->software_version); + version_warning = 1; + } + if (radio->hardware_version < RADIO_HW_VERSION) { + dev_warn(&intf->dev, + "This driver is known to work with " + "hardware version %hu,\n", RADIO_HW_VERSION); + dev_warn(&intf->dev, + "but the device has hardware version %hu.\n", + radio->hardware_version); + version_warning = 1; + } + + /* give out version warning */ + if (version_warning == 1) { + dev_warn(&intf->dev, + "If you have some trouble using this driver,\n"); + dev_warn(&intf->dev, + "please report to V4L ML at " + "linux-media@vger.kernel.org\n"); + } + + /* set initial frequency */ + si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + + /* set led to connect state */ + si470x_set_led_state(radio, BLINK_GREEN_LED); + + /* rds buffer allocation */ + radio->buf_size = rds_buf * 3; + radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); + if (!radio->buffer) { + retval = -EIO; + goto err_video; + } + + /* rds buffer configuration */ + radio->wr_index = 0; + radio->rd_index = 0; + init_waitqueue_head(&radio->read_queue); + + /* register video device */ + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr); + if (retval) { + dev_warn(&intf->dev, "Could not register video device\n"); + goto err_all; + } + usb_set_intfdata(intf, radio); + + return 0; +err_all: + kfree(radio->buffer); +err_video: + video_device_release(radio->videodev); +err_intbuffer: + kfree(radio->int_in_buffer); +err_radio: + kfree(radio); +err_initial: + return retval; +} + + +/* + * si470x_usb_driver_suspend - suspend the device + */ +static int si470x_usb_driver_suspend(struct usb_interface *intf, + pm_message_t message) +{ + dev_info(&intf->dev, "suspending now...\n"); + + return 0; +} + + +/* + * si470x_usb_driver_resume - resume the device + */ +static int si470x_usb_driver_resume(struct usb_interface *intf) +{ + dev_info(&intf->dev, "resuming now...\n"); + + return 0; +} + + +/* + * si470x_usb_driver_disconnect - disconnect the device + */ +static void si470x_usb_driver_disconnect(struct usb_interface *intf) +{ + struct si470x_device *radio = usb_get_intfdata(intf); + + mutex_lock(&radio->disconnect_lock); + radio->disconnected = 1; + usb_set_intfdata(intf, NULL); + if (radio->users == 0) { + /* set led to disconnect state */ + si470x_set_led_state(radio, BLINK_ORANGE_LED); + + /* Free data structures. */ + usb_free_urb(radio->int_in_urb); + + kfree(radio->int_in_buffer); + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + } + mutex_unlock(&radio->disconnect_lock); +} + + +/* + * si470x_usb_driver - usb driver interface + */ +static struct usb_driver si470x_usb_driver = { + .name = DRIVER_NAME, + .probe = si470x_usb_driver_probe, + .disconnect = si470x_usb_driver_disconnect, + .suspend = si470x_usb_driver_suspend, + .resume = si470x_usb_driver_resume, + .id_table = si470x_usb_driver_id_table, + .supports_autosuspend = 1, +}; + + + +/************************************************************************** + * Module Interface + **************************************************************************/ + +/* + * si470x_module_init - module init + */ +static int __init si470x_module_init(void) +{ + printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); + return usb_register(&si470x_usb_driver); +} + + +/* + * si470x_module_exit - module exit + */ +static void __exit si470x_module_exit(void) +{ + usb_deregister(&si470x_usb_driver); +} + + +module_init(si470x_module_init); +module_exit(si470x_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h new file mode 100644 index 00000000000..d0af194d194 --- /dev/null +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -0,0 +1,225 @@ +/* + * drivers/media/radio/si470x/radio-si470x.h + * + * Driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* driver definitions */ +#define DRIVER_NAME "radio-si470x" + + +/* kernel includes */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/input.h> +#include <linux/version.h> +#include <linux/videodev2.h> +#include <linux/mutex.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/rds.h> +#include <asm/unaligned.h> + + + +/************************************************************************** + * Register Definitions + **************************************************************************/ +#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */ +#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */ +#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */ + +#define DEVICEID 0 /* Device ID */ +#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ +#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ + +#define CHIPID 1 /* Chip ID */ +#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ +#define CHIPID_DEV 0x0200 /* bits 09..09: Device */ +#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ + +#define POWERCFG 2 /* Power Configuration */ +#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ +#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */ +#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */ +#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */ +#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */ +#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */ +#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */ +#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */ +#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */ + +#define CHANNEL 3 /* Channel */ +#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */ +#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */ + +#define SYSCONFIG1 4 /* System Configuration 1 */ +#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */ +#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */ +#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */ +#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */ +#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */ +#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ +#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ +#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ +#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ + +#define SYSCONFIG2 5 /* System Configuration 2 */ +#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ +#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ +#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ +#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ + +#define SYSCONFIG3 6 /* System Configuration 3 */ +#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */ +#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */ +#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */ +#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */ + +#define TEST1 7 /* Test 1 */ +#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */ + +#define TEST2 8 /* Test 2 */ +/* TEST2 only contains reserved bits */ + +#define BOOTCONFIG 9 /* Boot Configuration */ +/* BOOTCONFIG only contains reserved bits */ + +#define STATUSRSSI 10 /* Status RSSI */ +#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */ +#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */ +#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */ +#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */ +#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */ +#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */ +#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */ +#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */ + +#define READCHAN 11 /* Read Channel */ +#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */ +#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */ +#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */ +#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */ + +#define RDSA 12 /* RDSA */ +#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */ + +#define RDSB 13 /* RDSB */ +#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */ + +#define RDSC 14 /* RDSC */ +#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */ + +#define RDSD 15 /* RDSD */ +#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ + + + +/************************************************************************** + * General Driver Definitions + **************************************************************************/ + +/* + * si470x_device - private data + */ +struct si470x_device { + struct video_device *videodev; + + /* driver management */ + unsigned int users; + + /* Silabs internal registers (0..15) */ + unsigned short registers[RADIO_REGISTER_NUM]; + + /* RDS receive buffer */ + wait_queue_head_t read_queue; + struct mutex lock; /* buffer locking */ + unsigned char *buffer; /* size is always multiple of three */ + unsigned int buf_size; + unsigned int rd_index; + unsigned int wr_index; + +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + + /* Interrupt endpoint handling */ + char *int_in_buffer; + struct usb_endpoint_descriptor *int_in_endpoint; + struct urb *int_in_urb; + int int_in_running; + + /* scratch page */ + unsigned char software_version; + unsigned char hardware_version; + + /* driver management */ + unsigned char disconnected; + struct mutex disconnect_lock; +#endif + +#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) + struct i2c_client *client; +#endif +}; + + + +/************************************************************************** + * Firmware Versions + **************************************************************************/ + +#define RADIO_FW_VERSION 15 + + + +/************************************************************************** + * Frequency Multiplicator + **************************************************************************/ + +/* + * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, + * 62.5 kHz otherwise. + * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. + * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW + * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 + */ +#define FREQ_MUL (1000000 / 62.5) + + + +/************************************************************************** + * Common Functions + **************************************************************************/ +extern const struct v4l2_file_operations si470x_fops; +extern struct video_device si470x_viddev_template; +int si470x_get_register(struct si470x_device *radio, int regnr); +int si470x_set_register(struct si470x_device *radio, int regnr); +int si470x_disconnect_check(struct si470x_device *radio); +int si470x_set_freq(struct si470x_device *radio, unsigned int freq); +int si470x_start(struct si470x_device *radio); +int si470x_stop(struct si470x_device *radio); +int si470x_rds_on(struct si470x_device *radio); +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability); diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c new file mode 100644 index 00000000000..6a0028eb461 --- /dev/null +++ b/drivers/media/radio/si4713-i2c.c @@ -0,0 +1,2060 @@ +/* + * drivers/media/radio/si4713-i2c.c + * + * Silicon Labs Si4713 FM Radio Transmitter I2C commands. + * + * Copyright (c) 2009 Nokia Corporation + * Contact: Eduardo Valentin <eduardo.valentin@nokia.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> + +#include "si4713-i2c.h" + +/* module parameters */ +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>"); +MODULE_DESCRIPTION("I2C driver for Si4713 FM Radio Transmitter"); +MODULE_VERSION("0.0.1"); + +#define DEFAULT_RDS_PI 0x00 +#define DEFAULT_RDS_PTY 0x00 +#define DEFAULT_RDS_PS_NAME "" +#define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME +#define DEFAULT_RDS_DEVIATION 0x00C8 +#define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 +#define DEFAULT_LIMITER_RTIME 0x1392 +#define DEFAULT_LIMITER_DEV 0x102CA +#define DEFAULT_PILOT_FREQUENCY 0x4A38 +#define DEFAULT_PILOT_DEVIATION 0x1A5E +#define DEFAULT_ACOMP_ATIME 0x0000 +#define DEFAULT_ACOMP_RTIME 0xF4240L +#define DEFAULT_ACOMP_GAIN 0x0F +#define DEFAULT_ACOMP_THRESHOLD (-0x28) +#define DEFAULT_MUTE 0x01 +#define DEFAULT_POWER_LEVEL 88 +#define DEFAULT_FREQUENCY 8800 +#define DEFAULT_PREEMPHASIS FMPE_EU +#define DEFAULT_TUNE_RNL 0xFF + +#define to_si4713_device(sd) container_of(sd, struct si4713_device, sd) + +/* frequency domain transformation (using times 10 to avoid floats) */ +#define FREQDEV_UNIT 100000 +#define FREQV4L2_MULTI 625 +#define si4713_to_v4l2(f) ((f * FREQDEV_UNIT) / FREQV4L2_MULTI) +#define v4l2_to_si4713(f) ((f * FREQV4L2_MULTI) / FREQDEV_UNIT) +#define FREQ_RANGE_LOW 7600 +#define FREQ_RANGE_HIGH 10800 + +#define MAX_ARGS 7 + +#define RDS_BLOCK 8 +#define RDS_BLOCK_CLEAR 0x03 +#define RDS_BLOCK_LOAD 0x04 +#define RDS_RADIOTEXT_2A 0x20 +#define RDS_RADIOTEXT_BLK_SIZE 4 +#define RDS_RADIOTEXT_INDEX_MAX 0x0F +#define RDS_CARRIAGE_RETURN 0x0D + +#define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) + +#define get_status_bit(p, b, m) (((p) & (m)) >> (b)) +#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) + +#define ATTACK_TIME_UNIT 500 + +#define POWER_OFF 0x00 +#define POWER_ON 0x01 + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) +#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) +#define check_command_failed(status) (!(status & SI4713_CTS) || \ + (status & SI4713_ERR)) +/* mute definition */ +#define set_mute(p) ((p & 1) | ((p & 1) << 1)); +#define get_mute(p) (p & 0x01) + +#ifdef DEBUG +#define DBG_BUFFER(device, message, buffer, size) \ + { \ + int i; \ + char str[(size)*5]; \ + for (i = 0; i < size; i++) \ + sprintf(str + i * 5, " 0x%02x", buffer[i]); \ + v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ + } +#else +#define DBG_BUFFER(device, message, buffer, size) +#endif + +/* + * Values for limiter release time (sorted by second column) + * device release + * value time (us) + */ +static long limiter_times[] = { + 2000, 250, + 1000, 500, + 510, 1000, + 255, 2000, + 170, 3000, + 127, 4020, + 102, 5010, + 85, 6020, + 73, 7010, + 64, 7990, + 57, 8970, + 51, 10030, + 25, 20470, + 17, 30110, + 13, 39380, + 10, 51190, + 8, 63690, + 7, 73140, + 6, 85330, + 5, 102390, +}; + +/* + * Values for audio compression release time (sorted by second column) + * device release + * value time (us) + */ +static unsigned long acomp_rtimes[] = { + 0, 100000, + 1, 200000, + 2, 350000, + 3, 525000, + 4, 1000000, +}; + +/* + * Values for preemphasis (sorted by second column) + * device preemphasis + * value value (v4l2) + */ +static unsigned long preemphasis_values[] = { + FMPE_DISABLED, V4L2_PREEMPHASIS_DISABLED, + FMPE_EU, V4L2_PREEMPHASIS_50_uS, + FMPE_USA, V4L2_PREEMPHASIS_75_uS, +}; + +static int usecs_to_dev(unsigned long usecs, unsigned long const array[], + int size) +{ + int i; + int rval = -EINVAL; + + for (i = 0; i < size / 2; i++) + if (array[(i * 2) + 1] >= usecs) { + rval = array[i * 2]; + break; + } + + return rval; +} + +static unsigned long dev_to_usecs(int value, unsigned long const array[], + int size) +{ + int i; + int rval = -EINVAL; + + for (i = 0; i < size / 2; i++) + if (array[i * 2] == value) { + rval = array[(i * 2) + 1]; + break; + } + + return rval; +} + +/* si4713_handler: IRQ handler, just complete work */ +static irqreturn_t si4713_handler(int irq, void *dev) +{ + struct si4713_device *sdev = dev; + + v4l2_dbg(2, debug, &sdev->sd, + "%s: sending signal to completion work.\n", __func__); + complete(&sdev->work); + + return IRQ_HANDLED; +} + +/* + * si4713_send_command - sends a command to si4713 and waits its response + * @sdev: si4713_device structure for the device we are communicating + * @command: command id + * @args: command arguments we are sending (up to 7) + * @argn: actual size of @args + * @response: buffer to place the expected response from the device (up to 15) + * @respn: actual size of @response + * @usecs: amount of time to wait before reading the response (in usecs) + */ +static int si4713_send_command(struct si4713_device *sdev, const u8 command, + const u8 args[], const int argn, + u8 response[], const int respn, const int usecs) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); + u8 data1[MAX_ARGS + 1]; + int err; + + if (!client->adapter) + return -ENODEV; + + /* First send the command and its arguments */ + data1[0] = command; + memcpy(data1 + 1, args, argn); + DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); + + err = i2c_master_send(client, data1, argn + 1); + if (err != argn + 1) { + v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + /* Wait response from interrupt */ + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "(%s) Device took too much time to answer.\n", + __func__); + + /* Then get the response */ + err = i2c_master_recv(client, response, respn); + if (err != respn) { + v4l2_err(&sdev->sd, + "Error while reading response for command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + DBG_BUFFER(&sdev->sd, "Response", response, respn); + if (check_command_failed(response[0])) + return -EBUSY; + + return 0; +} + +/* + * si4713_read_property - reads a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @pv: property value to be returned on success + */ +static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv) +{ + int err; + u8 val[SI4713_GET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + */ + const u8 args[SI4713_GET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + }; + + err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + *pv = compose_u16(val[2], val[3]); + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, *pv, val[0]); + + return err; +} + +/* + * si4713_write_property - modifies a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @val: new value for that property + */ +static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val) +{ + int rval; + u8 resp[SI4713_SET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_SET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + msb(val), + lsb(val), + }; + + rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, val, resp[0]); + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +/* + * si4713_powerup - Powers the device up + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerup(struct si4713_device *sdev) +{ + int err; + u8 resp[SI4713_PWUP_NRESP]; + /* + * .First byte = Enabled interrupts and boot function + * .Second byte = Input operation mode + */ + const u8 args[SI4713_PWUP_NARGS] = { + SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, + SI4713_PWUP_OPMOD_ANALOG, + }; + + if (sdev->power_state) + return 0; + + sdev->platform_data->set_power(1); + err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + TIMEOUT_POWER_UP); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); + sdev->power_state = POWER_ON; + + err = si4713_write_property(sdev, SI4713_GPO_IEN, + SI4713_STC_INT | SI4713_CTS); + } else { + sdev->platform_data->set_power(0); + } + + return err; +} + +/* + * si4713_powerdown - Powers the device down + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerdown(struct si4713_device *sdev) +{ + int err; + u8 resp[SI4713_PWDN_NRESP]; + + if (!sdev->power_state) + return 0; + + err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); + sdev->platform_data->set_power(0); + sdev->power_state = POWER_OFF; + } + + return err; +} + +/* + * si4713_checkrev - Checks if we are treating a device with the correct rev. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_checkrev(struct si4713_device *sdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); + int rval; + u8 resp[SI4713_GETREV_NRESP]; + + mutex_lock(&sdev->mutex); + + rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + goto unlock; + + if (resp[1] == SI4713_PRODUCT_NUMBER) { + v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + } else { + v4l2_err(&sdev->sd, "Invalid product number\n"); + rval = -EINVAL; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* + * si4713_wait_stc - Waits STC interrupt and clears status bits. Usefull + * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS + * @sdev: si4713_device structure for the device we are communicating + * @usecs: timeout to wait for STC interrupt signal + */ +static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) +{ + int err; + u8 resp[SI4713_GET_STATUS_NRESP]; + + /* Wait response from STC interrupt */ + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "%s: device took too much time to answer (%d usec).\n", + __func__, usecs); + + /* Clear status bits */ + err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (err < 0) + goto exit; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: status bits: 0x%02x\n", __func__, resp[0]); + + if (!(resp[0] & SI4713_STC_INT)) + err = -EIO; + +exit: + return err; +} + +/* + * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning + * frequency between 76 and 108 MHz in 10 kHz units and + * steps of 50 kHz. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + */ +static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) +{ + int err; + u8 val[SI4713_TXFREQ_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + */ + const u8 args[SI4713_TXFREQ_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x status=0x%02x\n", __func__, + frequency, val[0]); + + err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); + if (err < 0) + return err; + + return compose_u16(args[1], args[2]); +} + +/* + * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in + * 1 dB units. A value of 0x00 indicates off. The command + * also sets the antenna tuning capacitance. A value of 0 + * indicates autotuning, and a value of 1 - 191 indicates + * a manual override, which results in a tuning + * capacitance of 0.25 pF x @antcap. + * @sdev: si4713_device structure for the device we are communicating + * @power: tuning power (88 - 115 dBuV, unit/step 1 dB) + * @antcap: value of antenna tuning capacitor (0 - 191) + */ +static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, + u8 antcap) +{ + int err; + u8 val[SI4713_TXPWR_NRESP]; + /* + * .First byte = 0 + * .Second byte = 0 + * .Third byte = power + * .Fourth byte = antcap + */ + const u8 args[SI4713_TXPWR_NARGS] = { + 0x00, + 0x00, + power, + antcap, + }; + + if (((power > 0) && (power < SI4713_MIN_POWER)) || + power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP) + return -EDOM; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, power, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_POWER); +} + +/* + * si4713_tx_tune_measure - Enters receive mode and measures the received noise + * level in units of dBuV on the selected frequency. + * The Frequency must be between 76 and 108 MHz in 10 kHz + * units and steps of 50 kHz. The command also sets the + * antenna tuning capacitance. A value of 0 means + * autotuning, and a value of 1 to 191 indicates manual + * override. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + * @antcap: value of antenna tuning capacitor (0 - 191) + */ +static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, + u8 antcap) +{ + int err; + u8 val[SI4713_TXMEA_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + * .Fourth byte = antcap + */ + const u8 args[SI4713_TXMEA_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + antcap, + }; + + sdev->tune_rnl = DEFAULT_TUNE_RNL; + + if (antcap > SI4713_MAX_ANTCAP) + return -EDOM; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, frequency, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); +} + +/* + * si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or + * tx_tune_power commands. This command return the current + * frequency, output voltage in dBuV, the antenna tunning + * capacitance value and the received noise level. The + * command also clears the stcint interrupt bit when the + * first bit of its arguments is high. + * @sdev: si4713_device structure for the device we are communicating + * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. + * @frequency: returned frequency + * @power: returned power + * @antcap: returned antenna capacitance + * @noise: returned noise level + */ +static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, + u16 *frequency, u8 *power, + u8 *antcap, u8 *noise) +{ + int err; + u8 val[SI4713_TXSTATUS_NRESP]; + /* + * .First byte = intack bit + */ + const u8 args[SI4713_TXSTATUS_NARGS] = { + intack & SI4713_INTACK_MASK, + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, + "%s: status=0x%02x\n", __func__, val[0]); + *frequency = compose_u16(val[2], val[3]); + sdev->frequency = *frequency; + *power = val[5]; + *antcap = val[6]; + *noise = val[7]; + v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz " + "(power %d, antcap %d, rnl %d)\n", __func__, + *frequency, *power, *antcap, *noise); + } + + return err; +} + +/* + * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer. + * @sdev: si4713_device structure for the device we are communicating + * @mode: the buffer operation mode. + * @rdsb: RDS Block B + * @rdsc: RDS Block C + * @rdsd: RDS Block D + * @cbleft: returns the number of available circular buffer blocks minus the + * number of used circular buffer blocks. + */ +static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb, + u16 rdsc, u16 rdsd, s8 *cbleft) +{ + int err; + u8 val[SI4713_RDSBUFF_NRESP]; + + const u8 args[SI4713_RDSBUFF_NARGS] = { + mode & SI4713_RDSBUFF_MODE_MASK, + msb(rdsb), + lsb(rdsb), + msb(rdsc), + lsb(rdsc), + msb(rdsd), + lsb(rdsd), + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, + "%s: status=0x%02x\n", __func__, val[0]); + *cbleft = (s8)val[2] - val[3]; + v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts" + " 0x%02x cb avail: %d cb used %d fifo avail" + " %d fifo used %d\n", __func__, val[1], + val[2], val[3], val[4], val[5]); + } + + return err; +} + +/* + * si4713_tx_rds_ps - Loads the program service buffer. + * @sdev: si4713_device structure for the device we are communicating + * @psid: program service id to be loaded. + * @pschar: assumed 4 size char array to be loaded into the program service + */ +static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, + unsigned char *pschar) +{ + int err; + u8 val[SI4713_RDSPS_NRESP]; + + const u8 args[SI4713_RDSPS_NARGS] = { + psid & SI4713_RDSPS_PSID_MASK, + pschar[0], + pschar[1], + pschar[2], + pschar[3], + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); + + return err; +} + +static int si4713_set_power_state(struct si4713_device *sdev, u8 value) +{ + int rval; + + mutex_lock(&sdev->mutex); + + if (value) + rval = si4713_powerup(sdev); + else + rval = si4713_powerdown(sdev); + + mutex_unlock(&sdev->mutex); + return rval; +} + +static int si4713_set_mute(struct si4713_device *sdev, u16 mute) +{ + int rval = 0; + + mute = set_mute(mute); + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) + rval = si4713_write_property(sdev, + SI4713_TX_LINE_INPUT_MUTE, mute); + + if (rval >= 0) + sdev->mute = get_mute(mute); + + mutex_unlock(&sdev->mutex); + + return rval; +} + +static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) +{ + int rval = 0, i; + u8 len = 0; + + /* We want to clear the whole thing */ + if (!strlen(ps_name)) + memset(ps_name, 0, MAX_RDS_PS_NAME + 1); + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + /* Write the new ps name and clear the padding */ + for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) { + rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)), + ps_name + i); + if (rval < 0) + goto unlock; + } + + /* Setup the size to be sent */ + if (strlen(ps_name)) + len = strlen(ps_name) - 1; + else + len = 1; + + rval = si4713_write_property(sdev, + SI4713_TX_RDS_PS_MESSAGE_COUNT, + rds_ps_nblocks(len)); + if (rval < 0) + goto unlock; + + rval = si4713_write_property(sdev, + SI4713_TX_RDS_PS_REPEAT_COUNT, + DEFAULT_RDS_PS_REPEAT_COUNT * 2); + if (rval < 0) + goto unlock; + } + + strncpy(sdev->rds_info.ps_name, ps_name, MAX_RDS_PS_NAME); + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) +{ + int rval = 0, i; + u16 t_index = 0; + u8 b_index = 0, cr_inserted = 0; + s8 left; + + mutex_lock(&sdev->mutex); + + if (!sdev->power_state) + goto copy; + + rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left); + if (rval < 0) + goto unlock; + + if (!strlen(rt)) + goto copy; + + do { + /* RDS spec says that if the last block isn't used, + * then apply a carriage return + */ + if (t_index < (RDS_RADIOTEXT_INDEX_MAX * + RDS_RADIOTEXT_BLK_SIZE)) { + for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) { + if (!rt[t_index + i] || rt[t_index + i] == + RDS_CARRIAGE_RETURN) { + rt[t_index + i] = RDS_CARRIAGE_RETURN; + cr_inserted = 1; + break; + } + } + } + + rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_LOAD, + compose_u16(RDS_RADIOTEXT_2A, b_index++), + compose_u16(rt[t_index], rt[t_index + 1]), + compose_u16(rt[t_index + 2], rt[t_index + 3]), + &left); + if (rval < 0) + goto unlock; + + t_index += RDS_RADIOTEXT_BLK_SIZE; + + if (cr_inserted) + break; + } while (left > 0); + +copy: + strncpy(sdev->rds_info.radio_text, rt, MAX_RDS_RADIO_TEXT); + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, + u32 **shadow, s32 *bit, s32 *mask, u16 *property, int *mul, + unsigned long **table, int *size) +{ + s32 rval = 0; + + switch (id) { + /* FM_TX class controls */ + case V4L2_CID_RDS_TX_PI: + *property = SI4713_TX_RDS_PI; + *mul = 1; + *shadow = &sdev->rds_info.pi; + break; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + *property = SI4713_TX_ACOMP_THRESHOLD; + *mul = 1; + *shadow = &sdev->acomp_info.threshold; + break; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + *property = SI4713_TX_ACOMP_GAIN; + *mul = 1; + *shadow = &sdev->acomp_info.gain; + break; + case V4L2_CID_PILOT_TONE_FREQUENCY: + *property = SI4713_TX_PILOT_FREQUENCY; + *mul = 1; + *shadow = &sdev->pilot_info.frequency; + break; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + *property = SI4713_TX_ACOMP_ATTACK_TIME; + *mul = ATTACK_TIME_UNIT; + *shadow = &sdev->acomp_info.attack_time; + break; + case V4L2_CID_PILOT_TONE_DEVIATION: + *property = SI4713_TX_PILOT_DEVIATION; + *mul = 10; + *shadow = &sdev->pilot_info.deviation; + break; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + *property = SI4713_TX_AUDIO_DEVIATION; + *mul = 10; + *shadow = &sdev->limiter_info.deviation; + break; + case V4L2_CID_RDS_TX_DEVIATION: + *property = SI4713_TX_RDS_DEVIATION; + *mul = 1; + *shadow = &sdev->rds_info.deviation; + break; + + case V4L2_CID_RDS_TX_PTY: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 5; + *mask = 0x1F << 5; + *shadow = &sdev->rds_info.pty; + break; + case V4L2_CID_AUDIO_LIMITER_ENABLED: + *property = SI4713_TX_ACOMP_ENABLE; + *bit = 1; + *mask = 1 << 1; + *shadow = &sdev->limiter_info.enabled; + break; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + *property = SI4713_TX_ACOMP_ENABLE; + *bit = 0; + *mask = 1 << 0; + *shadow = &sdev->acomp_info.enabled; + break; + case V4L2_CID_PILOT_TONE_ENABLED: + *property = SI4713_TX_COMPONENT_ENABLE; + *bit = 0; + *mask = 1 << 0; + *shadow = &sdev->pilot_info.enabled; + break; + + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + *property = SI4713_TX_LIMITER_RELEASE_TIME; + *table = limiter_times; + *size = ARRAY_SIZE(limiter_times); + *shadow = &sdev->limiter_info.release_time; + break; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + *property = SI4713_TX_ACOMP_RELEASE_TIME; + *table = acomp_rtimes; + *size = ARRAY_SIZE(acomp_rtimes); + *shadow = &sdev->acomp_info.release_time; + break; + case V4L2_CID_TUNE_PREEMPHASIS: + *property = SI4713_TX_PREEMPHASIS; + *table = preemphasis_values; + *size = ARRAY_SIZE(preemphasis_values); + *shadow = &sdev->preemphasis; + break; + + default: + rval = -EINVAL; + }; + + return rval; +} + +static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); + +/* write string property */ +static int si4713_write_econtrol_string(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + struct v4l2_queryctrl vqc; + int len; + s32 rval = 0; + + vqc.id = control->id; + rval = si4713_queryctrl(&sdev->sd, &vqc); + if (rval < 0) + goto exit; + + switch (control->id) { + case V4L2_CID_RDS_TX_PS_NAME: { + char ps_name[MAX_RDS_PS_NAME + 1]; + + len = control->size - 1; + if (len > MAX_RDS_PS_NAME) { + rval = -ERANGE; + goto exit; + } + rval = copy_from_user(ps_name, control->string, len); + if (rval < 0) + goto exit; + ps_name[len] = '\0'; + + if (strlen(ps_name) % vqc.step) { + rval = -ERANGE; + goto exit; + } + + rval = si4713_set_rds_ps_name(sdev, ps_name); + } + break; + + case V4L2_CID_RDS_TX_RADIO_TEXT: { + char radio_text[MAX_RDS_RADIO_TEXT + 1]; + + len = control->size - 1; + if (len > MAX_RDS_RADIO_TEXT) { + rval = -ERANGE; + goto exit; + } + rval = copy_from_user(radio_text, control->string, len); + if (rval < 0) + goto exit; + radio_text[len] = '\0'; + + if (strlen(radio_text) % vqc.step) { + rval = -ERANGE; + goto exit; + } + + rval = si4713_set_rds_radio_text(sdev, radio_text); + } + break; + + default: + rval = -EINVAL; + break; + }; + +exit: + return rval; +} + +static int validate_range(struct v4l2_subdev *sd, + struct v4l2_ext_control *control) +{ + struct v4l2_queryctrl vqc; + int rval; + + vqc.id = control->id; + rval = si4713_queryctrl(sd, &vqc); + if (rval < 0) + goto exit; + + if (control->value < vqc.minimum || control->value > vqc.maximum) + rval = -ERANGE; + +exit: + return rval; +} + +/* properties which use tx_tune_power*/ +static int si4713_write_econtrol_tune(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + s32 rval = 0; + u8 power, antcap; + + rval = validate_range(&sdev->sd, control); + if (rval < 0) + goto exit; + + mutex_lock(&sdev->mutex); + + switch (control->id) { + case V4L2_CID_TUNE_POWER_LEVEL: + power = control->value; + antcap = sdev->antenna_capacitor; + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + power = sdev->power_level; + antcap = control->value; + break; + default: + rval = -EINVAL; + goto unlock; + }; + + if (sdev->power_state) + rval = si4713_tx_tune_power(sdev, power, antcap); + + if (rval == 0) { + sdev->power_level = power; + sdev->antenna_capacitor = antcap; + } + +unlock: + mutex_unlock(&sdev->mutex); +exit: + return rval; +} + +static int si4713_write_econtrol_integers(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + s32 rval; + u32 *shadow = NULL, val = 0; + s32 bit = 0, mask = 0; + u16 property = 0; + int mul = 0; + unsigned long *table = NULL; + int size = 0; + + rval = validate_range(&sdev->sd, control); + if (rval < 0) + goto exit; + + rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, + &mask, &property, &mul, &table, &size); + if (rval < 0) + goto exit; + + val = control->value; + if (mul) { + val = control->value / mul; + } else if (table) { + rval = usecs_to_dev(control->value, table, size); + if (rval < 0) + goto exit; + val = rval; + rval = 0; + } + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + if (mask) { + rval = si4713_read_property(sdev, property, &val); + if (rval < 0) + goto unlock; + val = set_bits(val, control->value, bit, mask); + } + + rval = si4713_write_property(sdev, property, val); + if (rval < 0) + goto unlock; + if (mask) + val = control->value; + } + + if (mul) { + *shadow = val * mul; + } else if (table) { + rval = dev_to_usecs(val, table, size); + if (rval < 0) + goto unlock; + *shadow = rval; + rval = 0; + } else { + *shadow = val; + } + +unlock: + mutex_unlock(&sdev->mutex); +exit: + return rval; +} + +static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f); +static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *); +/* + * si4713_setup - Sets the device up with current configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_setup(struct si4713_device *sdev) +{ + struct v4l2_ext_control ctrl; + struct v4l2_frequency f; + struct v4l2_modulator vm; + struct si4713_device *tmp; + int rval = 0; + + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + /* Get a local copy to avoid race */ + mutex_lock(&sdev->mutex); + memcpy(tmp, sdev, sizeof(*sdev)); + mutex_unlock(&sdev->mutex); + + ctrl.id = V4L2_CID_RDS_TX_PI; + ctrl.value = tmp->rds_info.pi; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_COMPRESSION_THRESHOLD; + ctrl.value = tmp->acomp_info.threshold; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_COMPRESSION_GAIN; + ctrl.value = tmp->acomp_info.gain; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_PILOT_TONE_FREQUENCY; + ctrl.value = tmp->pilot_info.frequency; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME; + ctrl.value = tmp->acomp_info.attack_time; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_PILOT_TONE_DEVIATION; + ctrl.value = tmp->pilot_info.deviation; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_LIMITER_DEVIATION; + ctrl.value = tmp->limiter_info.deviation; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_RDS_TX_DEVIATION; + ctrl.value = tmp->rds_info.deviation; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_RDS_TX_PTY; + ctrl.value = tmp->rds_info.pty; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_LIMITER_ENABLED; + ctrl.value = tmp->limiter_info.enabled; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ENABLED; + ctrl.value = tmp->acomp_info.enabled; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_PILOT_TONE_ENABLED; + ctrl.value = tmp->pilot_info.enabled; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_LIMITER_RELEASE_TIME; + ctrl.value = tmp->limiter_info.release_time; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME; + ctrl.value = tmp->acomp_info.release_time; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_TUNE_PREEMPHASIS; + ctrl.value = tmp->preemphasis; + rval |= si4713_write_econtrol_integers(sdev, &ctrl); + + ctrl.id = V4L2_CID_RDS_TX_PS_NAME; + rval |= si4713_set_rds_ps_name(sdev, tmp->rds_info.ps_name); + + ctrl.id = V4L2_CID_RDS_TX_RADIO_TEXT; + rval |= si4713_set_rds_radio_text(sdev, tmp->rds_info.radio_text); + + /* Device procedure needs to set frequency first */ + f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY; + f.frequency = si4713_to_v4l2(f.frequency); + rval |= si4713_s_frequency(&sdev->sd, &f); + + ctrl.id = V4L2_CID_TUNE_POWER_LEVEL; + ctrl.value = tmp->power_level; + rval |= si4713_write_econtrol_tune(sdev, &ctrl); + + ctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR; + ctrl.value = tmp->antenna_capacitor; + rval |= si4713_write_econtrol_tune(sdev, &ctrl); + + vm.index = 0; + if (tmp->stereo) + vm.txsubchans = V4L2_TUNER_SUB_STEREO; + else + vm.txsubchans = V4L2_TUNER_SUB_MONO; + if (tmp->rds_info.enabled) + vm.txsubchans |= V4L2_TUNER_SUB_RDS; + si4713_s_modulator(&sdev->sd, &vm); + + kfree(tmp); + + return rval; +} + +/* + * si4713_initialize - Sets the device up with default configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_initialize(struct si4713_device *sdev) +{ + int rval; + + rval = si4713_set_power_state(sdev, POWER_ON); + if (rval < 0) + goto exit; + + rval = si4713_checkrev(sdev); + if (rval < 0) + goto exit; + + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + goto exit; + + mutex_lock(&sdev->mutex); + + sdev->rds_info.pi = DEFAULT_RDS_PI; + sdev->rds_info.pty = DEFAULT_RDS_PTY; + sdev->rds_info.deviation = DEFAULT_RDS_DEVIATION; + strlcpy(sdev->rds_info.ps_name, DEFAULT_RDS_PS_NAME, MAX_RDS_PS_NAME); + strlcpy(sdev->rds_info.radio_text, DEFAULT_RDS_RADIO_TEXT, + MAX_RDS_RADIO_TEXT); + sdev->rds_info.enabled = 1; + + sdev->limiter_info.release_time = DEFAULT_LIMITER_RTIME; + sdev->limiter_info.deviation = DEFAULT_LIMITER_DEV; + sdev->limiter_info.enabled = 1; + + sdev->pilot_info.deviation = DEFAULT_PILOT_DEVIATION; + sdev->pilot_info.frequency = DEFAULT_PILOT_FREQUENCY; + sdev->pilot_info.enabled = 1; + + sdev->acomp_info.release_time = DEFAULT_ACOMP_RTIME; + sdev->acomp_info.attack_time = DEFAULT_ACOMP_ATIME; + sdev->acomp_info.threshold = DEFAULT_ACOMP_THRESHOLD; + sdev->acomp_info.gain = DEFAULT_ACOMP_GAIN; + sdev->acomp_info.enabled = 1; + + sdev->frequency = DEFAULT_FREQUENCY; + sdev->preemphasis = DEFAULT_PREEMPHASIS; + sdev->mute = DEFAULT_MUTE; + sdev->power_level = DEFAULT_POWER_LEVEL; + sdev->antenna_capacitor = 0; + sdev->stereo = 1; + sdev->tune_rnl = DEFAULT_TUNE_RNL; + + mutex_unlock(&sdev->mutex); + +exit: + return rval; +} + +/* read string property */ +static int si4713_read_econtrol_string(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + s32 rval = 0; + + switch (control->id) { + case V4L2_CID_RDS_TX_PS_NAME: + if (strlen(sdev->rds_info.ps_name) + 1 > control->size) { + control->size = MAX_RDS_PS_NAME + 1; + rval = -ENOSPC; + goto exit; + } + rval = copy_to_user(control->string, sdev->rds_info.ps_name, + strlen(sdev->rds_info.ps_name) + 1); + break; + + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (strlen(sdev->rds_info.radio_text) + 1 > control->size) { + control->size = MAX_RDS_RADIO_TEXT + 1; + rval = -ENOSPC; + goto exit; + } + rval = copy_to_user(control->string, sdev->rds_info.radio_text, + strlen(sdev->rds_info.radio_text) + 1); + break; + + default: + rval = -EINVAL; + break; + }; + +exit: + return rval; +} + +/* + * si4713_update_tune_status - update properties from tx_tune_status + * command. Must be called with sdev->mutex held. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_update_tune_status(struct si4713_device *sdev) +{ + int rval; + u16 f = 0; + u8 p = 0, a = 0, n = 0; + + rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); + + if (rval < 0) + goto exit; + + sdev->power_level = p; + sdev->antenna_capacitor = a; + sdev->tune_rnl = n; + +exit: + return rval; +} + +/* properties which use tx_tune_status */ +static int si4713_read_econtrol_tune(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + s32 rval = 0; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_update_tune_status(sdev); + if (rval < 0) + goto unlock; + } + + switch (control->id) { + case V4L2_CID_TUNE_POWER_LEVEL: + control->value = sdev->power_level; + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + control->value = sdev->antenna_capacitor; + break; + default: + rval = -EINVAL; + }; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +static int si4713_read_econtrol_integers(struct si4713_device *sdev, + struct v4l2_ext_control *control) +{ + s32 rval; + u32 *shadow = NULL, val = 0; + s32 bit = 0, mask = 0; + u16 property = 0; + int mul = 0; + unsigned long *table = NULL; + int size = 0; + + rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, + &mask, &property, &mul, &table, &size); + if (rval < 0) + goto exit; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_read_property(sdev, property, &val); + if (rval < 0) + goto unlock; + + /* Keep negative values for threshold */ + if (control->id == V4L2_CID_AUDIO_COMPRESSION_THRESHOLD) + *shadow = (s16)val; + else if (mask) + *shadow = get_status_bit(val, bit, mask); + else if (mul) + *shadow = val * mul; + else + *shadow = dev_to_usecs(val, table, size); + } + + control->value = *shadow; + +unlock: + mutex_unlock(&sdev->mutex); +exit: + return rval; +} + +/* + * Video4Linux Subdev Interface + */ +/* si4713_s_ext_ctrls - set extended controls value */ +static int si4713_s_ext_ctrls(struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + int err; + + switch ((ctrls->controls + i)->id) { + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + err = si4713_write_econtrol_string(sdev, + ctrls->controls + i); + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + case V4L2_CID_TUNE_POWER_LEVEL: + err = si4713_write_econtrol_tune(sdev, + ctrls->controls + i); + break; + default: + err = si4713_write_econtrol_integers(sdev, + ctrls->controls + i); + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +/* si4713_g_ext_ctrls - get extended controls value */ +static int si4713_g_ext_ctrls(struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + int err; + + switch ((ctrls->controls + i)->id) { + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + err = si4713_read_econtrol_string(sdev, + ctrls->controls + i); + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + case V4L2_CID_TUNE_POWER_LEVEL: + err = si4713_read_econtrol_tune(sdev, + ctrls->controls + i); + break; + default: + err = si4713_read_econtrol_integers(sdev, + ctrls->controls + i); + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +/* si4713_queryctrl - enumerate control items */ +static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int rval = 0; + + switch (qc->id) { + /* User class controls */ + case V4L2_CID_AUDIO_MUTE: + rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, DEFAULT_MUTE); + break; + /* FM_TX class controls */ + case V4L2_CID_RDS_TX_PI: + rval = v4l2_ctrl_query_fill(qc, 0, 0xFFFF, 1, DEFAULT_RDS_PI); + break; + case V4L2_CID_RDS_TX_PTY: + rval = v4l2_ctrl_query_fill(qc, 0, 31, 1, DEFAULT_RDS_PTY); + break; + case V4L2_CID_RDS_TX_DEVIATION: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_DEVIATION, + 10, DEFAULT_RDS_DEVIATION); + break; + case V4L2_CID_RDS_TX_PS_NAME: + /* + * Report step as 8. From RDS spec, psname + * should be 8. But there are receivers which scroll strings + * sized as 8xN. + */ + rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_PS_NAME, 8, 0); + break; + case V4L2_CID_RDS_TX_RADIO_TEXT: + /* + * Report step as 32 (2A block). From RDS spec, + * radio text should be 32 for 2A block. But there are receivers + * which scroll strings sized as 32xN. Setting default to 32. + */ + rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_RADIO_TEXT, 32, 0); + break; + + case V4L2_CID_AUDIO_LIMITER_ENABLED: + rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + break; + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + rval = v4l2_ctrl_query_fill(qc, 250, MAX_LIMITER_RELEASE_TIME, + 50, DEFAULT_LIMITER_RTIME); + break; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_LIMITER_DEVIATION, + 10, DEFAULT_LIMITER_DEV); + break; + + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + break; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_GAIN, 1, + DEFAULT_ACOMP_GAIN); + break; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + rval = v4l2_ctrl_query_fill(qc, MIN_ACOMP_THRESHOLD, + MAX_ACOMP_THRESHOLD, 1, + DEFAULT_ACOMP_THRESHOLD); + break; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_ATTACK_TIME, + 500, DEFAULT_ACOMP_ATIME); + break; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + rval = v4l2_ctrl_query_fill(qc, 100000, MAX_ACOMP_RELEASE_TIME, + 100000, DEFAULT_ACOMP_RTIME); + break; + + case V4L2_CID_PILOT_TONE_ENABLED: + rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + break; + case V4L2_CID_PILOT_TONE_DEVIATION: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_DEVIATION, + 10, DEFAULT_PILOT_DEVIATION); + break; + case V4L2_CID_PILOT_TONE_FREQUENCY: + rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_FREQUENCY, + 1, DEFAULT_PILOT_FREQUENCY); + break; + + case V4L2_CID_TUNE_PREEMPHASIS: + rval = v4l2_ctrl_query_fill(qc, V4L2_PREEMPHASIS_DISABLED, + V4L2_PREEMPHASIS_75_uS, 1, + V4L2_PREEMPHASIS_50_uS); + break; + case V4L2_CID_TUNE_POWER_LEVEL: + rval = v4l2_ctrl_query_fill(qc, 0, 120, 1, DEFAULT_POWER_LEVEL); + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + rval = v4l2_ctrl_query_fill(qc, 0, 191, 1, 0); + break; + default: + rval = -EINVAL; + break; + }; + + return rval; +} + +/* si4713_g_ctrl - get the value of a control */ +static int si4713_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + if (!sdev) + return -ENODEV; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE, + &sdev->mute); + + if (rval < 0) + goto unlock; + } + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(sdev->mute); + break; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* si4713_s_ctrl - set the value of a control */ +static int si4713_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + if (!sdev) + return -ENODEV; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + rval = si4713_set_mute(sdev, ctrl->value); + if (rval < 0) + goto exit; + + rval = si4713_set_power_state(sdev, POWER_DOWN); + } else { + rval = si4713_set_power_state(sdev, POWER_UP); + if (rval < 0) + goto exit; + + rval = si4713_setup(sdev); + if (rval < 0) + goto exit; + + rval = si4713_set_mute(sdev, ctrl->value); + } + break; + } + +exit: + return rval; +} + +/* si4713_ioctl - deal with private ioctls (only rnl for now) */ +long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct si4713_device *sdev = to_si4713_device(sd); + struct si4713_rnl *rnl = arg; + u16 frequency; + int rval = 0; + + if (!arg) + return -EINVAL; + + mutex_lock(&sdev->mutex); + switch (cmd) { + case SI4713_IOC_MEASURE_RNL: + frequency = v4l2_to_si4713(rnl->frequency); + + if (sdev->power_state) { + /* Set desired measurement frequency */ + rval = si4713_tx_tune_measure(sdev, frequency, 0); + if (rval < 0) + goto unlock; + /* get results from tune status */ + rval = si4713_update_tune_status(sdev); + if (rval < 0) + goto unlock; + } + rnl->rnl = sdev->tune_rnl; + break; + + default: + /* nothing */ + rval = -ENOIOCTLCMD; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { + .queryctrl = si4713_queryctrl, + .g_ext_ctrls = si4713_g_ext_ctrls, + .s_ext_ctrls = si4713_s_ext_ctrls, + .g_ctrl = si4713_g_ctrl, + .s_ctrl = si4713_s_ctrl, + .ioctl = si4713_ioctl, +}; + +/* si4713_g_modulator - get modulator attributes */ +static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + if (!sdev) { + rval = -ENODEV; + goto exit; + } + + if (vm->index > 0) { + rval = -EINVAL; + goto exit; + } + + strncpy(vm->name, "FM Modulator", 32); + vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_RDS; + + /* Report current frequency range limits */ + vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); + vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + u32 comp_en = 0; + + rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, + &comp_en); + if (rval < 0) + goto unlock; + + sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); + sdev->rds_info.enabled = get_status_bit(comp_en, 2, 1 << 2); + } + + /* Report current audio mode: mono or stereo */ + if (sdev->stereo) + vm->txsubchans = V4L2_TUNER_SUB_STEREO; + else + vm->txsubchans = V4L2_TUNER_SUB_MONO; + + /* Report rds feature status */ + if (sdev->rds_info.enabled) + vm->txsubchans |= V4L2_TUNER_SUB_RDS; + else + vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; + +unlock: + mutex_unlock(&sdev->mutex); +exit: + return rval; +} + +/* si4713_s_modulator - set modulator attributes */ +static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + u16 stereo, rds; + u32 p; + + if (!sdev) + return -ENODEV; + + if (vm->index > 0) + return -EINVAL; + + /* Set audio mode: mono or stereo */ + if (vm->txsubchans & V4L2_TUNER_SUB_STEREO) + stereo = 1; + else if (vm->txsubchans & V4L2_TUNER_SUB_MONO) + stereo = 0; + else + return -EINVAL; + + rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_read_property(sdev, + SI4713_TX_COMPONENT_ENABLE, &p); + if (rval < 0) + goto unlock; + + p = set_bits(p, stereo, 1, 1 << 1); + p = set_bits(p, rds, 2, 1 << 2); + + rval = si4713_write_property(sdev, + SI4713_TX_COMPONENT_ENABLE, p); + if (rval < 0) + goto unlock; + } + + sdev->stereo = stereo; + sdev->rds_info.enabled = rds; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* si4713_g_frequency - get tuner or modulator radio frequency */ +static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + + f->type = V4L2_TUNER_RADIO; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + u16 freq; + u8 p, a, n; + + rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); + if (rval < 0) + goto unlock; + + sdev->frequency = freq; + } + + f->frequency = si4713_to_v4l2(sdev->frequency); + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* si4713_s_frequency - set tuner or modulator radio frequency */ +static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct si4713_device *sdev = to_si4713_device(sd); + int rval = 0; + u16 frequency = v4l2_to_si4713(f->frequency); + + /* Check frequency range */ + if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) + return -EDOM; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_tx_tune_freq(sdev, frequency); + if (rval < 0) + goto unlock; + frequency = rval; + rval = 0; + } + sdev->frequency = frequency; + f->frequency = si4713_to_v4l2(frequency); + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = { + .g_frequency = si4713_g_frequency, + .s_frequency = si4713_s_frequency, + .g_modulator = si4713_g_modulator, + .s_modulator = si4713_s_modulator, +}; + +static const struct v4l2_subdev_ops si4713_subdev_ops = { + .core = &si4713_subdev_core_ops, + .tuner = &si4713_subdev_tuner_ops, +}; + +/* + * I2C driver interface + */ +/* si4713_probe - probe for the device */ +static int si4713_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si4713_device *sdev; + int rval; + + sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + if (!sdev) { + dev_err(&client->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + + sdev->platform_data = client->dev.platform_data; + if (!sdev->platform_data) { + v4l2_err(&sdev->sd, "No platform data registered.\n"); + rval = -ENODEV; + goto free_sdev; + } + + v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops); + + mutex_init(&sdev->mutex); + init_completion(&sdev->work); + + if (client->irq) { + rval = request_irq(client->irq, + si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->name, sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Could not request IRQ\n"); + goto free_sdev; + } + v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); + } else { + v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); + } + + rval = si4713_initialize(sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Failed to probe device information.\n"); + goto free_irq; + } + + return 0; + +free_irq: + if (client->irq) + free_irq(client->irq, sdev); +free_sdev: + kfree(sdev); +exit: + return rval; +} + +/* si4713_remove - remove the device */ +static int si4713_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct si4713_device *sdev = to_si4713_device(sd); + + if (sdev->power_state) + si4713_set_power_state(sdev, POWER_DOWN); + + if (client->irq > 0) + free_irq(client->irq, sdev); + + v4l2_device_unregister_subdev(sd); + + kfree(sdev); + + return 0; +} + +/* si4713_i2c_driver - i2c driver interface */ +static const struct i2c_device_id si4713_id[] = { + { "si4713" , 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, si4713_id); + +static struct i2c_driver si4713_i2c_driver = { + .driver = { + .name = "si4713", + }, + .probe = si4713_probe, + .remove = si4713_remove, + .id_table = si4713_id, +}; + +/* Module Interface */ +static int __init si4713_module_init(void) +{ + return i2c_add_driver(&si4713_i2c_driver); +} + +static void __exit si4713_module_exit(void) +{ + i2c_del_driver(&si4713_i2c_driver); +} + +module_init(si4713_module_init); +module_exit(si4713_module_exit); + diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713-i2c.h new file mode 100644 index 00000000000..faf8cff124f --- /dev/null +++ b/drivers/media/radio/si4713-i2c.h @@ -0,0 +1,237 @@ +/* + * drivers/media/radio/si4713-i2c.h + * + * Property and commands definitions for Si4713 radio transmitter chip. + * + * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT + * Contact: Eduardo Valentin <eduardo.valentin@nokia.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef SI4713_I2C_H +#define SI4713_I2C_H + +#include <media/v4l2-subdev.h> +#include <media/si4713.h> + +#define SI4713_PRODUCT_NUMBER 0x0D + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT 500 +#define TIMEOUT_SET_PROPERTY 20 +#define TIMEOUT_TX_TUNE_POWER 30000 +#define TIMEOUT_TX_TUNE 110000 +#define TIMEOUT_POWER_UP 200000 + +/* + * Command and its arguments definitions + */ +#define SI4713_PWUP_CTSIEN (1<<7) +#define SI4713_PWUP_GPO2OEN (1<<6) +#define SI4713_PWUP_PATCH (1<<5) +#define SI4713_PWUP_XOSCEN (1<<4) +#define SI4713_PWUP_FUNC_TX 0x02 +#define SI4713_PWUP_FUNC_PATCH 0x0F +#define SI4713_PWUP_OPMOD_ANALOG 0x50 +#define SI4713_PWUP_OPMOD_DIGITAL 0x0F +#define SI4713_PWUP_NARGS 2 +#define SI4713_PWUP_NRESP 1 +#define SI4713_CMD_POWER_UP 0x01 + +#define SI4713_GETREV_NRESP 9 +#define SI4713_CMD_GET_REV 0x10 + +#define SI4713_PWDN_NRESP 1 +#define SI4713_CMD_POWER_DOWN 0x11 + +#define SI4713_SET_PROP_NARGS 5 +#define SI4713_SET_PROP_NRESP 1 +#define SI4713_CMD_SET_PROPERTY 0x12 + +#define SI4713_GET_PROP_NARGS 3 +#define SI4713_GET_PROP_NRESP 4 +#define SI4713_CMD_GET_PROPERTY 0x13 + +#define SI4713_GET_STATUS_NRESP 1 +#define SI4713_CMD_GET_INT_STATUS 0x14 + +#define SI4713_CMD_PATCH_ARGS 0x15 +#define SI4713_CMD_PATCH_DATA 0x16 + +#define SI4713_MAX_FREQ 10800 +#define SI4713_MIN_FREQ 7600 +#define SI4713_TXFREQ_NARGS 3 +#define SI4713_TXFREQ_NRESP 1 +#define SI4713_CMD_TX_TUNE_FREQ 0x30 + +#define SI4713_MAX_POWER 120 +#define SI4713_MIN_POWER 88 +#define SI4713_MAX_ANTCAP 191 +#define SI4713_MIN_ANTCAP 0 +#define SI4713_TXPWR_NARGS 4 +#define SI4713_TXPWR_NRESP 1 +#define SI4713_CMD_TX_TUNE_POWER 0x31 + +#define SI4713_TXMEA_NARGS 4 +#define SI4713_TXMEA_NRESP 1 +#define SI4713_CMD_TX_TUNE_MEASURE 0x32 + +#define SI4713_INTACK_MASK 0x01 +#define SI4713_TXSTATUS_NARGS 1 +#define SI4713_TXSTATUS_NRESP 8 +#define SI4713_CMD_TX_TUNE_STATUS 0x33 + +#define SI4713_OVERMOD_BIT (1 << 2) +#define SI4713_IALH_BIT (1 << 1) +#define SI4713_IALL_BIT (1 << 0) +#define SI4713_ASQSTATUS_NARGS 1 +#define SI4713_ASQSTATUS_NRESP 5 +#define SI4713_CMD_TX_ASQ_STATUS 0x34 + +#define SI4713_RDSBUFF_MODE_MASK 0x87 +#define SI4713_RDSBUFF_NARGS 7 +#define SI4713_RDSBUFF_NRESP 6 +#define SI4713_CMD_TX_RDS_BUFF 0x35 + +#define SI4713_RDSPS_PSID_MASK 0x1F +#define SI4713_RDSPS_NARGS 5 +#define SI4713_RDSPS_NRESP 1 +#define SI4713_CMD_TX_RDS_PS 0x36 + +#define SI4713_CMD_GPO_CTL 0x80 +#define SI4713_CMD_GPO_SET 0x81 + +/* + * Bits from status response + */ +#define SI4713_CTS (1<<7) +#define SI4713_ERR (1<<6) +#define SI4713_RDS_INT (1<<2) +#define SI4713_ASQ_INT (1<<1) +#define SI4713_STC_INT (1<<0) + +/* + * Property definitions + */ +#define SI4713_GPO_IEN 0x0001 +#define SI4713_DIG_INPUT_FORMAT 0x0101 +#define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 +#define SI4713_REFCLK_FREQ 0x0201 +#define SI4713_REFCLK_PRESCALE 0x0202 +#define SI4713_TX_COMPONENT_ENABLE 0x2100 +#define SI4713_TX_AUDIO_DEVIATION 0x2101 +#define SI4713_TX_PILOT_DEVIATION 0x2102 +#define SI4713_TX_RDS_DEVIATION 0x2103 +#define SI4713_TX_LINE_INPUT_LEVEL 0x2104 +#define SI4713_TX_LINE_INPUT_MUTE 0x2105 +#define SI4713_TX_PREEMPHASIS 0x2106 +#define SI4713_TX_PILOT_FREQUENCY 0x2107 +#define SI4713_TX_ACOMP_ENABLE 0x2200 +#define SI4713_TX_ACOMP_THRESHOLD 0x2201 +#define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 +#define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 +#define SI4713_TX_ACOMP_GAIN 0x2204 +#define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 +#define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 +#define SI4713_TX_ASQ_LEVEL_LOW 0x2301 +#define SI4713_TX_ASQ_DURATION_LOW 0x2302 +#define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 +#define SI4713_TX_ASQ_DURATION_HIGH 0x2304 +#define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 +#define SI4713_TX_RDS_PI 0x2C01 +#define SI4713_TX_RDS_PS_MIX 0x2C02 +#define SI4713_TX_RDS_PS_MISC 0x2C03 +#define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 +#define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 +#define SI4713_TX_RDS_PS_AF 0x2C06 +#define SI4713_TX_RDS_FIFO_SIZE 0x2C07 + +#define PREEMPHASIS_USA 75 +#define PREEMPHASIS_EU 50 +#define PREEMPHASIS_DISABLED 0 +#define FMPE_USA 0x00 +#define FMPE_EU 0x01 +#define FMPE_DISABLED 0x02 + +#define POWER_UP 0x01 +#define POWER_DOWN 0x00 + +struct rds_info { + u32 pi; +#define MAX_RDS_PTY 31 + u32 pty; +#define MAX_RDS_DEVIATION 90000 + u32 deviation; +/* + * PSNAME is known to be defined as 8 character sized (RDS Spec). + * However, there is receivers which scroll PSNAME 8xN sized. + */ +#define MAX_RDS_PS_NAME 96 + u8 ps_name[MAX_RDS_PS_NAME + 1]; +/* + * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) + * character sized (RDS Spec). + * However, there is receivers which scroll them as well. + */ +#define MAX_RDS_RADIO_TEXT 384 + u8 radio_text[MAX_RDS_RADIO_TEXT + 1]; + u32 enabled; +}; + +struct limiter_info { +#define MAX_LIMITER_RELEASE_TIME 102390 + u32 release_time; +#define MAX_LIMITER_DEVIATION 90000 + u32 deviation; + u32 enabled; +}; + +struct pilot_info { +#define MAX_PILOT_DEVIATION 90000 + u32 deviation; +#define MAX_PILOT_FREQUENCY 19000 + u32 frequency; + u32 enabled; +}; + +struct acomp_info { +#define MAX_ACOMP_RELEASE_TIME 1000000 + u32 release_time; +#define MAX_ACOMP_ATTACK_TIME 5000 + u32 attack_time; +#define MAX_ACOMP_THRESHOLD 0 +#define MIN_ACOMP_THRESHOLD (-40) + s32 threshold; +#define MAX_ACOMP_GAIN 20 + u32 gain; + u32 enabled; +}; + +/* + * si4713_device - private data + */ +struct si4713_device { + /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ + struct v4l2_subdev sd; + /* private data structures */ + struct mutex mutex; + struct completion work; + struct si4713_platform_data *platform_data; + struct rds_info rds_info; + struct limiter_info limiter_info; + struct pilot_info pilot_info; + struct acomp_info acomp_info; + u32 frequency; + u32 preemphasis; + u32 mute; + u32 power_level; + u32 power_state; + u32 antenna_capacitor; + u32 stereo; + u32 tune_rnl; +}; +#endif /* ifndef SI4713_I2C_H */ diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 84b6fc15519..e6186b338a1 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -203,9 +203,9 @@ config VIDEO_CS53L32A module will be called cs53l32a. config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - ---help--- + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_V4L2 && I2C + ---help--- Support for the Mitsubishi M52790 A/V switch. To compile this driver as a module, choose M here: the @@ -265,6 +265,15 @@ config VIDEO_SAA6588 comment "Video decoders" +config VIDEO_ADV7180 + tristate "Analog Devices ADV7180 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7180 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7180. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C @@ -493,6 +502,39 @@ config VIDEO_UPD64083 endmenu # encoder / decoder chips +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + select VIDEO_ADV7343 + select VIDEO_THS7303 + help + Support for DM6467 based display device. + + To compile this driver as a module, choose M here: the + module will be called vpif_display. + +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + help + Support for DM6467 based capture device. + + To compile this driver as a module, choose M here: the + module will be called vpif_capture. + +config VIDEO_DAVINCI_VPIF + tristate "DaVinci VPIF Driver" + depends on DISPLAY_DAVINCI_DM646X_EVM + help + Support for DaVinci VPIF Driver. + + To compile this driver as a module, choose M here: the + module will be called vpif. + config VIDEO_VIVI tristate "Virtual Video Driver" depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 @@ -505,6 +547,55 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on ARCH_DAVINCI + help + Support for vpss system module for video driver + default y + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG + help + Support for DMXXXX VPFE based frame grabber. This is the + common V4L2 module for following DMXXX SoCs from Texas + Instruments:- DM6446 & DM355. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS @@ -690,6 +781,8 @@ source "drivers/media/video/ivtv/Kconfig" source "drivers/media/video/cx18/Kconfig" +source "drivers/media/video/saa7164/Kconfig" + config VIDEO_M32R_AR tristate "AR devices" depends on M32R && VIDEO_V4L1 @@ -920,6 +1013,8 @@ source "drivers/media/video/pwc/Kconfig" config USB_ZR364XX tristate "USB ZR364XX Camera support" depends on VIDEO_V4L2 + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9f2e3214a48..e541932a789 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_BT819) += bt819.o @@ -154,12 +155,17 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ +obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c new file mode 100644 index 00000000000..1b3cbd02a7f --- /dev/null +++ b/drivers/media/video/adv7180.c @@ -0,0 +1,202 @@ +/* + * adv7180.c Analog Devices ADV7180 video decoder driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <media/v4l2-ioctl.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#define DRIVER_NAME "adv7180" + +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f + + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 +#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 +#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 +#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 +#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 +#define ADV7180_STATUS1_AUTOD_SECAM 0x50 +#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 +#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 + +#define ADV7180_IDENT_REG 0x11 +#define ADV7180_ID_7180 0x18 + + +struct adv7180_state { + struct v4l2_subdev sd; +}; + +static v4l2_std_id determine_norm(struct i2c_client *client) +{ + u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { + case ADV7180_STATUS1_AUTOD_NTSM_M_J: + return V4L2_STD_NTSC_M_JP; + case ADV7180_STATUS1_AUTOD_NTSC_4_43: + return V4L2_STD_NTSC_443; + case ADV7180_STATUS1_AUTOD_PAL_M: + return V4L2_STD_PAL_M; + case ADV7180_STATUS1_AUTOD_PAL_60: + return V4L2_STD_PAL_60; + case ADV7180_STATUS1_AUTOD_PAL_B_G: + return V4L2_STD_PAL; + case ADV7180_STATUS1_AUTOD_SECAM: + return V4L2_STD_SECAM; + case ADV7180_STATUS1_AUTOD_PAL_COMB: + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + case ADV7180_STATUS1_AUTOD_SECAM_525: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7180_state, sd); +} + +static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + *std = determine_norm(client); + return 0; +} + +static int adv7180_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); +} + +static const struct v4l2_subdev_video_ops adv7180_video_ops = { + .querystd = adv7180_querystd, +}; + +static const struct v4l2_subdev_core_ops adv7180_core_ops = { + .g_chip_ident = adv7180_g_chip_ident, +}; + +static const struct v4l2_subdev_ops adv7180_ops = { + .core = &adv7180_core_ops, + .video = &adv7180_video_ops, +}; + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7180_state *state; + struct v4l2_subdev *sd; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + + /* Initialize adv7180 */ + /* enable autodetection */ + ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM); + if (ret > 0) + ret = i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) { + printk(KERN_ERR DRIVER_NAME + ": Failed to communicate to chip: %d\n", ret); + return ret; + } + + return 0; +} + +static int adv7180_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id adv7180_id[] = { + {DRIVER_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7180_id); + +static struct i2c_driver adv7180_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = adv7180_probe, + .remove = adv7180_remove, + .id_table = adv7180_id, +}; + +static __init int adv7180_init(void) +{ + return i2c_add_driver(&adv7180_driver); +} + +static __exit void adv7180_exit(void) +{ + i2c_del_driver(&adv7180_driver); +} + +module_init(adv7180_init); +module_exit(adv7180_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 30f5caf5dda..df26f2fe44e 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/videodev2.h> #include <linux/uaccess.h> -#include <linux/version.h> #include <media/adv7343.h> #include <media/v4l2-device.h> diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 830c4a933f6..57dd9195daf 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -212,7 +212,7 @@ void au0828_card_setup(struct au0828_dev *dev) be abstracted out if we ever need to support a different demod) */ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "au8522", "au8522", 0x8e >> 1); + "au8522", "au8522", 0x8e >> 1, NULL); if (sd == NULL) printk(KERN_ERR "analog subdev registration failed\n"); } @@ -221,7 +221,7 @@ void au0828_card_setup(struct au0828_dev *dev) if (dev->board.tuner_type != TUNER_ABSENT) { /* Load the tuner module, which does the attach */ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", "tuner", dev->board.tuner_addr); + "tuner", "tuner", dev->board.tuner_addr, NULL); if (sd == NULL) printk(KERN_ERR "tuner subdev registration fail\n"); diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 14baffc2219..b8a4b52e8d4 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -151,7 +151,7 @@ static int start_urb_transfer(struct au0828_dev *dev) dprintk(2, "%s()\n", __func__); if (dev->urb_streaming) { - dprintk(2, "%s: iso xfer already running!\n", __func__); + dprintk(2, "%s: bulk xfer already running!\n", __func__); return 0; } diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c index 13e494365e7..cbdb65c34f2 100644 --- a/drivers/media/video/au0828/au0828-i2c.c +++ b/drivers/media/video/au0828/au0828-i2c.c @@ -320,7 +320,6 @@ static struct i2c_algorithm au0828_i2c_algo_template = { static struct i2c_adapter au0828_i2c_adap_template = { .name = DRIVER_NAME, .owner = THIS_MODULE, - .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, }; diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index ca6558c394b..12279f6d9bc 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -1274,6 +1274,7 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, + .has_remote = 1, }, /* ---- card 0x3c ---------------------------------- */ @@ -3523,8 +3524,8 @@ void __devinit bttv_init_card2(struct bttv *btv) }; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "saa6588", "saa6588", addrs); + sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "saa6588", "saa6588", 0, addrs); btv->has_saa6588 = (sd != NULL); } @@ -3548,8 +3549,8 @@ void __devinit bttv_init_card2(struct bttv *btv) I2C_CLIENT_END }; - btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "msp3400", "msp3400", addrs); + btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "msp3400", "msp3400", 0, addrs); if (btv->sd_msp34xx) return; goto no_audio; @@ -3562,16 +3563,16 @@ void __devinit bttv_init_card2(struct bttv *btv) I2C_CLIENT_END }; - if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tda7432", "tda7432", addrs)) + if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs)) return; goto no_audio; } case 3: { /* The user specified that we should probe for tvaudio */ - btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs()); + btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs()); if (btv->sd_tvaudio) return; goto no_audio; @@ -3590,13 +3591,13 @@ void __devinit bttv_init_card2(struct bttv *btv) it really is a msp3400, so it will return NULL when the device found is really something else (e.g. a tea6300). */ if (!bttv_tvcards[btv->c.type].no_msp34xx) { - btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev, + btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, &btv->c.i2c_adap, "msp3400", "msp3400", - I2C_ADDR_MSP3400 >> 1); + 0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1)); } else if (bttv_tvcards[btv->c.type].msp34xx_alt) { - btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev, + btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, &btv->c.i2c_adap, "msp3400", "msp3400", - I2C_ADDR_MSP3400_ALT >> 1); + 0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1)); } /* If we found a msp34xx, then we're done. */ @@ -3610,14 +3611,14 @@ void __devinit bttv_init_card2(struct bttv *btv) I2C_CLIENT_END }; - if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tda7432", "tda7432", addrs)) + if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs)) return; } /* Now see if we can find one of the tvaudio devices. */ - btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs()); + btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs()); if (btv->sd_tvaudio) return; @@ -3640,15 +3641,15 @@ void __devinit bttv_init_tuner(struct bttv *btv) /* Load tuner module before issuing tuner config call! */ if (bttv_tvcards[btv->c.type].has_radio) - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + v4l2_i2c_new_subdev(&btv->c.v4l2_dev, &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_RADIO)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_subdev(&btv->c.v4l2_dev, &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_subdev(&btv->c.v4l2_dev, &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); + 0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = btv->tuner_type; diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 8cc6dd28d6a..939d1e51297 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2652,6 +2652,8 @@ static int bttv_querycap(struct file *file, void *priv, V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (btv->has_saa6588) + cap->capabilities |= V4L2_CAP_RDS_CAPTURE; if (no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; @@ -4593,14 +4595,10 @@ static int bttv_resume(struct pci_dev *pci_dev) #endif static struct pci_device_id bttv_pci_tbl[] = { - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0}, {0,} }; diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index ebd1ee9dc87..beda363418b 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -352,7 +352,6 @@ int __devinit init_bttv_i2c(struct bttv *btv) /* bt878 */ strlcpy(btv->c.i2c_adap.name, "bt878", sizeof(btv->c.i2c_adap.name)); - btv->c.i2c_adap.id = I2C_HW_B_BT848; /* FIXME */ btv->c.i2c_adap.algo = &bttv_algo; } else { /* bt848 */ @@ -362,7 +361,6 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); - btv->c.i2c_adap.id = I2C_HW_B_BT848; memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, sizeof(bttv_i2c_algo_bit_template)); btv->i2c_algo.udelay = i2c_udelay; diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 2f289d981fe..ebd51afe876 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -245,7 +245,7 @@ static void bttv_ir_stop(struct bttv *btv) int bttv_input_init(struct bttv *btv) { struct card_ir *ir; - IR_KEYTAB_TYPE *ir_codes = NULL; + struct ir_scancode_table *ir_codes = NULL; struct input_dev *input_dev; int ir_type = IR_TYPE_OTHER; int err = -ENOMEM; @@ -263,7 +263,7 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVERMEDIA: case BTTV_BOARD_AVPHONE98: case BTTV_BOARD_AVERMEDIA98: - ir_codes = ir_codes_avermedia; + ir_codes = &ir_codes_avermedia_table; ir->mask_keycode = 0xf88000; ir->mask_keydown = 0x010000; ir->polling = 50; // ms @@ -271,14 +271,14 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVDVBT_761: case BTTV_BOARD_AVDVBT_771: - ir_codes = ir_codes_avermedia_dvbt; + ir_codes = &ir_codes_avermedia_dvbt_table; ir->mask_keycode = 0x0f00c0; ir->mask_keydown = 0x000020; ir->polling = 50; // ms break; case BTTV_BOARD_PXELVWPLTVPAK: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; ir->mask_keycode = 0x003e00; ir->mask_keyup = 0x010000; ir->polling = 50; // ms @@ -286,54 +286,55 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_PV_M4900: case BTTV_BOARD_PV_BT878P_9B: case BTTV_BOARD_PV_BT878P_PLUS: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; case BTTV_BOARD_WINFAST2000: - ir_codes = ir_codes_winfast; + ir_codes = &ir_codes_winfast_table; ir->mask_keycode = 0x1f8; break; case BTTV_BOARD_MAGICTVIEW061: case BTTV_BOARD_MAGICTVIEW063: - ir_codes = ir_codes_winfast; + ir_codes = &ir_codes_winfast_table; ir->mask_keycode = 0x0008e000; ir->mask_keydown = 0x00200000; break; case BTTV_BOARD_APAC_VIEWCOMP: - ir_codes = ir_codes_apac_viewcomp; + ir_codes = &ir_codes_apac_viewcomp_table; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; + case BTTV_BOARD_ASKEY_CPH03X: case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: case BTTV_BOARD_CONTVFMI: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x006000; ir->polling = 50; // ms break; case BTTV_BOARD_NEBULA_DIGITV: - ir_codes = ir_codes_nebula; + ir_codes = &ir_codes_nebula_table; btv->custom_irq = bttv_rc5_irq; ir->rc5_gpio = 1; break; case BTTV_BOARD_MACHTV_MAGICTV: - ir_codes = ir_codes_apac_viewcomp; + ir_codes = &ir_codes_apac_viewcomp_table; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x004000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_KOZUMI_KTV_01C: - ir_codes = ir_codes_pctv_sedna; + ir_codes = &ir_codes_pctv_sedna_table; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x006000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_ENLTV_FM_2: - ir_codes = ir_codes_encore_enltv2; + ir_codes = &ir_codes_encore_enltv2_table; ir->mask_keycode = 0x00fd00; ir->mask_keyup = 0x000080; ir->polling = 1; /* ms */ diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 10dbd4a11b3..9e39bc5f7b0 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -992,7 +992,7 @@ static int accept_bwqcam(struct parport *port) if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { /* user gave parport parameters */ - for(n=0; parport[n] && n<MAX_CAMS; n++){ + for (n = 0; n < MAX_CAMS && parport[n]; n++) { char *ep; unsigned long r; r = simple_strtoul(parport[n], &ep, 0); diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index c4d181dde1c..657c481d255 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -490,7 +490,6 @@ static int cafe_smbus_setup(struct cafe_camera *cam) int ret; cafe_smbus_enable_irq(cam); - adap->id = I2C_HW_SMBUS_CAFE; adap->owner = THIS_MODULE; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); @@ -1956,7 +1955,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam->sensor_addr = 0x42; cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter, - "ov7670", "ov7670", cam->sensor_addr); + "ov7670", "ov7670", cam->sensor_addr, NULL); if (cam->sensor == NULL) { ret = -ENODEV; goto out_smbus; diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 36f2d76006f..f11e47a5828 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -56,7 +56,8 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -102,7 +103,8 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -204,7 +206,7 @@ static const struct cx18_card cx18_card_mpc718 = { .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_GPIO_MUX, - .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | + .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 3c552b6b7c4..444e3c7c563 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -22,13 +22,17 @@ */ /* hardware flags */ -#define CX18_HW_TUNER (1 << 0) -#define CX18_HW_TVEEPROM (1 << 1) -#define CX18_HW_CS5345 (1 << 2) -#define CX18_HW_DVB (1 << 3) -#define CX18_HW_418_AV (1 << 4) -#define CX18_HW_GPIO_MUX (1 << 5) -#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_TUNER (1 << 0) +#define CX18_HW_TVEEPROM (1 << 1) +#define CX18_HW_CS5345 (1 << 2) +#define CX18_HW_DVB (1 << 3) +#define CX18_HW_418_AV (1 << 4) +#define CX18_HW_GPIO_MUX (1 << 5) +#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7) +#define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8) +#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 5136df19833..93f0dae0135 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ +#include <linux/kernel.h> #include "cx18-driver.h" #include "cx18-cards.h" @@ -317,7 +318,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); return err; } diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 92026e82e10..6dd51e27582 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -231,7 +231,7 @@ MODULE_PARM_DESC(enc_pcm_bufs, "Number of encoder PCM buffers\n" "\t\t\tDefault is computed from other enc_pcm_* parameters"); -MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("CX23418 driver"); @@ -268,6 +268,20 @@ static void cx18_iounmap(struct cx18 *cx) } } +static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len) +{ + int i; + + CX18_INFO("eeprom dump:\n"); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + CX18_INFO("eeprom %02x:", i); + printk(KERN_CONT " %02x", eedata[i]); + if (15 == (i % 16)) + printk(KERN_CONT "\n"); + } +} + /* Hauppauge card? get values from tveeprom */ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) { @@ -279,8 +293,26 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) c.adapter = &cx->i2c_adap[0]; c.addr = 0xA0 >> 1; - tveeprom_read(&c, eedata, sizeof(eedata)); - tveeprom_hauppauge_analog(&c, tv, eedata); + memset(tv, 0, sizeof(*tv)); + if (tveeprom_read(&c, eedata, sizeof(eedata))) + return; + + switch (cx->card->type) { + case CX18_CARD_HVR_1600_ESMT: + case CX18_CARD_HVR_1600_SAMSUNG: + tveeprom_hauppauge_analog(&c, tv, eedata); + break; + case CX18_CARD_YUAN_MPC718: + tv->model = 0x718; + cx18_eeprom_dump(cx, eedata, sizeof(eedata)); + CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n", + eedata[2], eedata[1], eedata[4], eedata[3]); + break; + default: + tv->model = 0xffffffff; + cx18_eeprom_dump(cx, eedata, sizeof(eedata)); + break; + } } static void cx18_process_eeprom(struct cx18 *cx) @@ -298,6 +330,11 @@ static void cx18_process_eeprom(struct cx18 *cx) case 74000 ... 74999: cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); break; + case 0x718: + return; + case 0xffffffff: + CX18_INFO("Unknown EEPROM encoding\n"); + return; case 0: CX18_ERR("Invalid EEPROM\n"); return; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 29969c18949..04d9c2508b8 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -690,7 +690,7 @@ int cx18_v4l2_open(struct file *filp) int res; struct video_device *video_dev = video_devdata(filp); struct cx18_stream *s = video_get_drvdata(video_dev); - struct cx18 *cx = s->cx;; + struct cx18 *cx = s->cx; mutex_lock(&cx->serialize_lock); if (cx18_init_on_first_open(cx)) { diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 8591e4fc359..2477461e84d 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -28,6 +28,7 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" +#include <media/ir-kbd-i2c.h> #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -40,16 +41,20 @@ #define GETSDL_BIT 0x0008 #define CX18_CS5345_I2C_ADDR 0x4c +#define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the CX18_HW_ defines */ static const u8 hw_addrs[] = { - 0, /* CX18_HW_TUNER */ - 0, /* CX18_HW_TVEEPROM */ - CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ - 0, /* CX18_HW_DVB */ - 0, /* CX18_HW_418_AV */ - 0, /* CX18_HW_GPIO_MUX */ - 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_TUNER */ + 0, /* CX18_HW_TVEEPROM */ + CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ + 0, /* CX18_HW_DVB */ + 0, /* CX18_HW_418_AV */ + 0, /* CX18_HW_GPIO_MUX */ + 0, /* CX18_HW_GPIO_RESET_CTRL */ + CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -62,6 +67,8 @@ static const u8 hw_bus[] = { 0, /* CX18_HW_418_AV */ 0, /* CX18_HW_GPIO_MUX */ 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -73,6 +80,8 @@ static const char * const hw_modules[] = { NULL, /* CX18_HW_418_AV */ NULL, /* CX18_HW_GPIO_MUX */ NULL, /* CX18_HW_GPIO_RESET_CTRL */ + NULL, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -84,8 +93,38 @@ static const char * const hw_devicenames[] = { "cx23418_AV", "gpio_mux", "gpio_reset_ctrl", + "ir_tx_z8f0811_haup", + "ir_rx_z8f0811_haup", }; +static const struct IR_i2c_init_data z8f0811_ir_init_data = { + .ir_codes = &ir_codes_hauppauge_new_table, + .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR, + .type = IR_TYPE_RC5, + .name = "CX23418 Z8F0811 Hauppauge", +}; + +static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, + u8 addr) +{ + struct i2c_board_info info; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case CX18_HW_Z8F0811_IR_RX_HAUP: + info.platform_data = (void *) &z8f0811_ir_init_data; + break; + default: + break; + } + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} + int cx18_i2c_register(struct cx18 *cx, unsigned idx) { struct v4l2_subdev *sd; @@ -100,27 +139,30 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) if (hw == CX18_HW_TUNER) { /* special tuner group handling */ - sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, - adap, mod, type, cx->card_i2c->radio); + sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, + adap, mod, type, 0, cx->card_i2c->radio); if (sd != NULL) sd->grp_id = hw; - sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, - adap, mod, type, cx->card_i2c->demod); + sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, + adap, mod, type, 0, cx->card_i2c->demod); if (sd != NULL) sd->grp_id = hw; - sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, - adap, mod, type, cx->card_i2c->tv); + sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, + adap, mod, type, 0, cx->card_i2c->tv); if (sd != NULL) sd->grp_id = hw; return sd != NULL ? 0 : -1; } + if (hw & CX18_HW_Z8F0811_IR_HAUP) + return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; - /* It's an I2C device other than an analog tuner */ - sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]); + /* It's an I2C device other than an analog tuner or IR chip */ + sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx], NULL); if (sd != NULL) sd->grp_id = hw; return sd != NULL ? 0 : -1; @@ -190,7 +232,6 @@ static int cx18_getsda(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter cx18_i2c_adap_template = { .name = "cx18 i2c driver", - .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index d7b1921e666..fc76e4d6ffa 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -605,7 +605,7 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) if (ret) return ret; - if (inp < 0 || inp >= cx->nof_inputs) + if (inp >= cx->nof_inputs) return -EINVAL; if (inp == cx->active_input) { diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 54d248e16d8..7df513a2dba 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -245,9 +245,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type) video_set_drvdata(s->video_dev, s); /* Register device. First try the desired minor, then any free one. */ - ret = video_register_device(s->video_dev, vfl_type, num); + ret = video_register_device_no_warn(s->video_dev, vfl_type, num); if (ret < 0) { - CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->video_dev); s->video_dev = NULL; diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 63d2239fd32..319c459459e 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -313,7 +313,7 @@ void cx231xx_card_setup(struct cx231xx *dev) if (dev->board.decoder == CX231XX_AVDECODER) { dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[0].i2c_adap, - "cx25840", "cx25840", 0x88 >> 1); + "cx25840", "cx25840", 0x88 >> 1, NULL); if (dev->sd_cx25840 == NULL) cx231xx_info("cx25840 subdev registration failure\n"); cx25840_call(dev, core, load_fw); @@ -323,7 +323,7 @@ void cx231xx_card_setup(struct cx231xx *dev) if (dev->board.tuner_type != TUNER_ABSENT) { dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[1].i2c_adap, - "tuner", "tuner", 0xc2 >> 1); + "tuner", "tuner", 0xc2 >> 1, NULL); if (dev->sd_tuner == NULL) cx231xx_info("tuner subdev registration failure\n"); diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h index a6f398a175c..31a8759f6e5 100644 --- a/drivers/media/video/cx231xx/cx231xx-conf-reg.h +++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h @@ -60,10 +60,10 @@ #define PWR_RESETOUT_EN 0x100 /* bit8 */ enum AV_MODE{ - POLARIS_AVMODE_DEFAULT = 0, - POLARIS_AVMODE_DIGITAL = 0x10, - POLARIS_AVMODE_ANALOGT_TV = 0x20, - POLARIS_AVMODE_ENXTERNAL_AV = 0x30, + POLARIS_AVMODE_DEFAULT = 0, + POLARIS_AVMODE_DIGITAL = 0x10, + POLARIS_AVMODE_ANALOGT_TV = 0x20, + POLARIS_AVMODE_ENXTERNAL_AV = 0x30, }; diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c index 33219dc4d64..58d9cc0867b 100644 --- a/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -432,7 +432,6 @@ static struct i2c_algorithm cx231xx_algo = { static struct i2c_adapter cx231xx_adap_template = { .owner = THIS_MODULE, .name = "cx231xx", - .id = I2C_HW_B_CX231XX, .algo = &cx231xx_algo, }; diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 609bae6098d..36503725d97 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -923,8 +923,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc;; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index a0f823ac6b8..64e2ddd3c40 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -282,7 +282,7 @@ struct cx231xx_board { struct cx231xx_input input[MAX_CX231XX_INPUT]; struct cx231xx_input radio; - IR_KEYTAB_TYPE *ir_codes; + struct ir_scancode_table *ir_codes; }; /* device states */ diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c index 08582e58bdb..c04222ffb28 100644 --- a/drivers/media/video/cx23885/cimax2.c +++ b/drivers/media/video/cx23885/cimax2.c @@ -75,7 +75,6 @@ struct netup_ci_state { void *priv; }; -struct mutex gpio_mutex;/* Two CiMax's uses same GPIO lines */ int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, u8 *buf, int len) @@ -183,10 +182,11 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, if (ret != 0) return ret; - mutex_lock(&gpio_mutex); + mutex_lock(&dev->gpio_lock); /* write addr */ cx_write(MC417_OEN, NETUP_EN_ALL); + msleep(2); cx_write(MC417_RWD, NETUP_CTRL_OFF | NETUP_ADLO | (0xff & addr)); cx_clear(MC417_RWD, NETUP_ADLO); @@ -194,9 +194,10 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, NETUP_ADHI | (0xff & (addr >> 8))); cx_clear(MC417_RWD, NETUP_ADHI); - if (read) /* data in */ + if (read) { /* data in */ cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); - else /* data out */ + msleep(2); + } else /* data out */ cx_write(MC417_RWD, NETUP_CTRL_OFF | data); /* choose chip */ @@ -206,7 +207,7 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); mem = netup_ci_get_mem(dev); - mutex_unlock(&gpio_mutex); + mutex_unlock(&dev->gpio_lock); if (!read) if (mem < 0) @@ -403,7 +404,6 @@ int netup_ci_init(struct cx23885_tsport *port) switch (port->nr) { case 1: state->ci_i2c_addr = 0x40; - mutex_init(&gpio_mutex); break; case 2: state->ci_i2c_addr = 0x41; @@ -443,6 +443,7 @@ int netup_ci_init(struct cx23885_tsport *port) goto err; INIT_WORK(&state->work, netup_read_ci_status); + schedule_work(&state->work); ci_dbg_print("%s: CI initialized!\n", __func__); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index e0cf21e0b1b..6c3b51ce337 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -630,6 +630,39 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) return retval; } +void mc417_gpio_set(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Set the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val |= (mask & 0x000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Clear the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val &= ~(mask & 0x0000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) +{ + u32 val; + + /* Enable GPIO direction bits */ + mc417_register_read(dev, 0x9020, &val); + if (asoutput) + val |= (mask & 0x0000ffff); + else + val &= ~(mask & 0x0000ffff); + + mc417_register_write(dev, 0x9020, val); +} /* ------------------------------------------------------------------ */ /* MPEG encoder API */ @@ -955,25 +988,8 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); - /* Restore GPIO settings, make sure EIO14 is enabled as an output. */ - dprintk(2, "%s: GPIO output EIO 0-15 was = 0x%x\n", - __func__, gpio_output); - /* Power-up seems to have GPIOs AFU. This was causing digital side - * to fail at power-up. Seems GPIOs should be set to 0x10ff0411 at - * power-up. - * gpio_output |= (1<<14); - */ - /* Note: GPIO14 is specific to the HVR1800 here */ - gpio_output = 0x10ff0411 | (1<<14); - retval |= mc417_register_write(dev, 0x9020, gpio_output | (1<<14)); - dprintk(2, "%s: GPIO output EIO 0-15 now = 0x%x\n", - __func__, gpio_output); - - dprintk(1, "%s: GPIO value EIO 0-15 was = 0x%x\n", - __func__, value); - value |= (1<<14); - dprintk(1, "%s: GPIO value EIO 0-15 now = 0x%x\n", - __func__, value); + /* F/W power up disturbs the GPIOs, restore state */ + retval |= mc417_register_write(dev, 0x9020, gpio_output); retval |= mc417_register_write(dev, 0x900C, value); retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); @@ -1715,6 +1731,8 @@ static struct video_device cx23885_mpeg_template = { .fops = &mpeg_fops, .ioctl_ops = &mpeg_ioctl_ops, .minor = -1, + .tvnorms = CX23885_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; void cx23885_417_unregister(struct cx23885_dev *dev) @@ -1786,9 +1804,6 @@ int cx23885_417_register(struct cx23885_dev *dev) return err; } - /* Initialize MC417 registers */ - cx23885_mc417_init(dev); - printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name, dev->v4l_device->num); diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index ce29b5e34a1..bfdf79f1033 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -201,6 +201,19 @@ struct cx23885_board cx23885_boards[] = { .name = "Mygica X8506 DMB-TH", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { + .name = "Magic-Pro ProHDTV Extreme 2", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1850] = { + .name = "Hauppauge WinTV-HVR1850", + .portb = CX23885_MPEG_ENCODER, + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = { + .name = "Compro VideoMate E800", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -324,6 +337,18 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8651, .card = CX23885_BOARD_MYGICA_X8506, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8657, + .card = CX23885_BOARD_MAGICPRO_PROHDTVE2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8541, + .card = CX23885_BOARD_HAUPPAUGE_HVR1850, + }, { + .subvendor = 0x1858, + .subdevice = 0xe800, + .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -483,8 +508,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) * DVB-T and MPEG2 HW Encoder */ break; + case 85021: + /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + Dual channel ATSC and MPEG2 HW Encoder */ + break; default: - printk(KERN_WARNING "%s: warning: unknown hauppauge model #%d\n", + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", dev->name, tv.model); break; } @@ -514,6 +544,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: + case CX23885_BOARD_COMPRO_VIDEOMATE_E800: /* Tuner Reset Command */ bitmask = 0x04; break; @@ -574,13 +605,23 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* CX23417 GPIO's */ /* EIO15 Zilog Reset */ /* EIO14 S5H1409/CX24227 Reset */ + mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_15 | GPIO_14); + mdelay(100); + + /* Bring the demod and blaster out of reset */ + mc417_gpio_set(dev, GPIO_15 | GPIO_14); + mdelay(100); /* Force the TDA8295A into reset and back */ - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_enable(dev, GPIO_2, 1); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); - cx_clear(GP0_IO, 0x00000004); + cx23885_gpio_clear(dev, GPIO_2); mdelay(20); - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); break; case CX23885_BOARD_HAUPPAUGE_HVR1200: @@ -655,6 +696,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: + case CX23885_BOARD_COMPRO_VIDEOMATE_E800: /* GPIO-2 xc3028 tuner reset */ /* The following GPIO's are on the internal AVCore (cx25840) */ @@ -715,14 +757,45 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_9); break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: /* GPIO-1 reset XC5000 */ - /* GPIO-2 reset LGS8GL5 */ + /* GPIO-2 reset LGS8GL5 / LGS8G75 */ cx_set(GP0_IO, 0x00060000); cx_clear(GP0_IO, 0x00000006); mdelay(100); cx_set(GP0_IO, 0x00060006); mdelay(100); break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + /* GPIO-0 656_CLK */ + /* GPIO-1 656_D0 */ + /* GPIO-2 Wake# */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + /* GPIO-19 IR_RX */ + /* GPIO-20 C_IR_TX */ + /* GPIO-21 I2S DAT */ + /* GPIO-22 I2S WCLK */ + /* GPIO-23 I2S BCLK */ + /* ALT GPIO: EXP GPIO LATCH */ + + /* CX23417 GPIO's */ + /* GPIO-14 S5H1411/CX24228 Reset */ + /* GPIO-13 EEPROM write protect */ + mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_14 | GPIO_13); + mdelay(100); + + /* Bring the demod out of reset */ + mc417_gpio_set(dev, GPIO_14); + mdelay(100); + + /* CX24228 GPIO */ + /* Connected to IF / Mux */ + break; } } @@ -739,6 +812,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -778,6 +852,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -827,6 +902,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -844,6 +920,8 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_COMPRO_VIDEOMATE_E800: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -860,9 +938,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + case CX23885_BOARD_COMPRO_VIDEOMATE_E800: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, - "cx25840", "cx25840", 0x88 >> 1); + "cx25840", "cx25840", 0x88 >> 1, NULL); v4l2_subdev_call(dev->sd_cx25840, core, load_fw); break; } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index bf7bb1c412f..c31284ba19d 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -713,12 +713,26 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) dev->hwrevision = 0xa1; break; case 0x02: - /* CX23885-13Z */ + /* CX23885-13Z/14Z */ dev->hwrevision = 0xb0; break; case 0x03: - /* CX23888-22Z */ - dev->hwrevision = 0xc0; + if (dev->pci->device == 0x8880) { + /* CX23888-21Z/22Z */ + dev->hwrevision = 0xc0; + } else { + /* CX23885-14Z */ + dev->hwrevision = 0xa4; + } + break; + case 0x04: + if (dev->pci->device == 0x8880) { + /* CX23888-31Z */ + dev->hwrevision = 0xd0; + } else { + /* CX23885-15Z, CX23888-31Z */ + dev->hwrevision = 0xa5; + } break; case 0x0e: /* CX23887-15Z */ @@ -744,6 +758,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) int i; mutex_init(&dev->lock); + mutex_init(&dev->gpio_lock); atomic_inc(&dev->refcount); @@ -756,6 +771,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) /* Configure the internal memory */ if (dev->pci->device == 0x8880) { + /* Could be 887 or 888, assume a default */ dev->bridge = CX23885_BRIDGE_887; /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 25000000; @@ -868,6 +884,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", __func__, dev->radio_type, dev->radio_addr); + /* The cx23417 encoder has GPIO's that need to be initialised + * before DVB, so that demodulators and tuners are out of + * reset before DVB uses them. + */ + if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) || + (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) + cx23885_mc417_init(dev); + /* init hardware */ cx23885_reset(dev); @@ -1250,6 +1274,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, switch (dev->bridge) { case CX23885_BRIDGE_885: case CX23885_BRIDGE_887: + case CX23885_BRIDGE_888: /* enable irqs */ dprintk(1, "%s() enabling TS int's and DMA\n", __func__); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 86ac529e62b..45e13ee66dc 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -255,15 +255,18 @@ static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { .std_map = &hauppauge_hvr1200_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1210_tuner_config = { .gate = TDA18271_GATE_DIGITAL, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_std_map hauppauge_hvr127x_std_map = { @@ -275,6 +278,7 @@ static struct tda18271_std_map hauppauge_hvr127x_std_map = { static struct tda18271_config hauppauge_hvr127x_config = { .std_map = &hauppauge_hvr127x_std_map, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct lgdt3305_config hauppauge_lgdt3305_config = { @@ -396,7 +400,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, - .xtal = 27000000, + .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ .ts_config_regs = stv0900_ts_regs, @@ -408,14 +412,14 @@ static struct stv0900_config netup_stv0900_config = { static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, - .mclk = 27000000, - .iq_wiring = 0, + .mclk = 16000000, + .clk_div = 1, }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, - .mclk = 27000000, - .iq_wiring = 1, + .mclk = 16000000, + .clk_div = 1, }; static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) @@ -487,6 +491,26 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, port->set_frontend_save(fe, param) : -ENODEV; } +static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { + .prod = LGS8GXX_PROD_LGS8G75, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 6500, /* 6.50 MHz */ + .if_neg_center = 1, + .ext_adc = 0, + .adc_signed = 1, + .adc_vpp = 2, /* 1.6 Vpp */ + .if_neg_edge = 1, +}; + +static struct xc5000_config magicpro_prohdtve2_xc5000_config = { + .i2c_address = 0x61, + .if_khz = 6500, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -723,6 +747,7 @@ static int dvb_register(struct cx23885_tsport *port) } case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: + case CX23885_BOARD_COMPRO_VIDEOMATE_E800: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(zl10353_attach, @@ -833,6 +858,30 @@ static int dvb_register(struct cx23885_tsport *port) &mygica_x8506_xc5000_config); } break; + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &magicpro_prohdtve2_lgs8g75_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus2->i2c_adap, + &magicpro_prohdtve2_xc5000_config); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &hcw_s5h1411_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[0].i2c_adap, + &hauppauge_tda18271_config); + break; + default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index 384dec34134..4172cb38742 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -283,7 +283,6 @@ static struct i2c_algorithm cx23885_i2c_algo_template = { static struct i2c_adapter cx23885_i2c_adap_template = { .name = "cx23885", .owner = THIS_MODULE, - .id = I2C_HW_B_CX23885, .algo = &cx23885_i2c_algo_template, }; diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 5d609333630..654cc253cd5 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -1521,11 +1521,11 @@ int cx23885_video_register(struct cx23885_dev *dev) if (dev->tuner_addr) sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[1].i2c_adap, - "tuner", "tuner", dev->tuner_addr); + "tuner", "tuner", dev->tuner_addr, NULL); else - sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[1].i2c_adap, - "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV)); + "tuner", "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV)); if (sd) { struct tuner_setup tun_setup; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 214a55e943b..cc7a165561f 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -76,6 +76,9 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1255 20 #define CX23885_BOARD_HAUPPAUGE_HVR1210 21 #define CX23885_BOARD_MYGICA_X8506 22 +#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 +#define CX23885_BOARD_HAUPPAUGE_HVR1850 24 +#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -87,6 +90,12 @@ #define GPIO_7 0x00000080 #define GPIO_8 0x00000100 #define GPIO_9 0x00000200 +#define GPIO_10 0x00000400 +#define GPIO_11 0x00000800 +#define GPIO_12 0x00001000 +#define GPIO_13 0x00002000 +#define GPIO_14 0x00004000 +#define GPIO_15 0x00008000 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -317,6 +326,7 @@ struct cx23885_dev { int nr; struct mutex lock; + struct mutex gpio_lock; /* board details */ unsigned int board; @@ -331,6 +341,7 @@ struct cx23885_dev { CX23885_BRIDGE_UNDEFINED = 0, CX23885_BRIDGE_885 = 885, CX23885_BRIDGE_887 = 887, + CX23885_BRIDGE_888 = 888, } bridge; /* Analog video */ @@ -395,7 +406,7 @@ struct sram_channel { u32 cmds_start; u32 ctrl_start; u32 cdt; - u32 fifo_start;; + u32 fifo_start; u32 fifo_size; u32 ptr1_reg; u32 ptr2_reg; @@ -504,6 +515,9 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev); extern void cx23885_mc417_init(struct cx23885_dev *dev); extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); +extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/cx23885/netup-eeprom.c b/drivers/media/video/cx23885/netup-eeprom.c index 042bbbbd48f..98a48f50068 100644 --- a/drivers/media/video/cx23885/netup-eeprom.c +++ b/drivers/media/video/cx23885/netup-eeprom.c @@ -97,11 +97,11 @@ void netup_get_card_info(struct i2c_adapter *i2c_adap, { int i, j; - cinfo->rev = netup_eeprom_read(i2c_adap, 13); + cinfo->rev = netup_eeprom_read(i2c_adap, 63); - for (i = 0, j = 0; i < 6; i++, j++) + for (i = 64, j = 0; i < 70; i++, j++) cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i); - for (i = 6, j = 0; i < 12; i++, j++) + for (i = 70, j = 0; i < 76; i++, j++) cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i); }; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 0be51b65f09..1aeaf18a9be 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -321,6 +321,15 @@ static void cx23885_initialize(struct i2c_client *client) /* Select AFE clock pad output source */ cx25840_write(client, 0x144, 0x05); + /* Drive GPIO2 direction and values for HVR1700 + * where an onboard mux selects the output of demodulator + * vs the 417. Failure to set this results in no DTV. + * It's safe to set this across all Hauppauge boards + * currently, regardless of the board type. + */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + /* Do the firmware load in a work handler to prevent. Otherwise the kernel is blocked waiting for the bit-banging i2c interface to finish uploading the @@ -1578,12 +1587,6 @@ static int cx25840_probe(struct i2c_client *client, state->id = id; state->rev = device_id; - if (state->is_cx23885) { - /* Drive GPIO2 direction and values */ - cx25840_write(client, 0x160, 0x1d); - cx25840_write(client, 0x164, 0x00); - } - return 0; } diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 0df53b0d75d..1f483c1d0db 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -23,10 +23,6 @@ #include "cx25840-core.h" -#define FWFILE "v4l-cx25840.fw" -#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw" -#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw" - /* * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the * size of the firmware chunks sent down the I2C bus to the chip. @@ -40,11 +36,11 @@ #define FWDEV(x) &((x)->dev) -static char *firmware = FWFILE; +static char *firmware = ""; module_param(firmware, charp, 0444); -MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); +MODULE_PARM_DESC(firmware, "Firmware image to load"); static void start_fw_load(struct i2c_client *client) { @@ -65,6 +61,19 @@ static void end_fw_load(struct i2c_client *client) cx25840_write(client, 0x803, 0x03); } +static const char *get_fw_name(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (firmware[0]) + return firmware; + if (state->is_cx23885) + return "v4l-cx23885-avcore-01.fw"; + if (state->is_cx231xx) + return "v4l-cx231xx-avcore-01.fw"; + return "v4l-cx25840.fw"; +} + static int check_fw_load(struct i2c_client *client, int size) { /* DL_ADDR_HB DL_ADDR_LB */ @@ -72,11 +81,13 @@ static int check_fw_load(struct i2c_client *client, int size) s |= cx25840_read(client, 0x800); if (size != s) { - v4l_err(client, "firmware %s load failed\n", firmware); + v4l_err(client, "firmware %s load failed\n", + get_fw_name(client)); return -EINVAL; } - v4l_info(client, "loaded %s firmware (%d bytes)\n", firmware, size); + v4l_info(client, "loaded %s firmware (%d bytes)\n", + get_fw_name(client), size); return 0; } @@ -96,21 +107,24 @@ int cx25840_loadfw(struct i2c_client *client) const struct firmware *fw = NULL; u8 buffer[FWSEND]; const u8 *ptr; + const char *fwname = get_fw_name(client); int size, retval; int MAX_BUF_SIZE = FWSEND; + u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) - firmware = FWFILE_CX23885; - else if (state->is_cx231xx) - firmware = FWFILE_CX231XX; + if (state->is_cx23885) { + /* Preserve the GPIO OE and output bits */ + gpio_oe = cx25840_read(client, 0x160); + gpio_da = cx25840_read(client, 0x164); + } if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ } - if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { - v4l_err(client, "unable to open firmware %s\n", firmware); + if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { + v4l_err(client, "unable to open firmware %s\n", fwname); return -EINVAL; } @@ -142,5 +156,11 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); + if (state->is_cx23885) { + /* Restore GPIO configuration after f/w load */ + cx25840_write(client, 0x160, gpio_oe); + cx25840_write(client, 0x164, gpio_da); + } + return check_fw_load(client, size); } diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 356d6896da3..fbdc1cde56a 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1371,7 +1371,7 @@ static struct cx8802_driver cx8802_blackbird_driver = { .advise_release = cx8802_blackbird_advise_release, }; -static int blackbird_init(void) +static int __init blackbird_init(void) { printk(KERN_INFO "cx2388x blackbird driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, @@ -1384,7 +1384,7 @@ static int blackbird_init(void) return cx8802_register_driver(&cx8802_blackbird_driver); } -static void blackbird_fini(void) +static void __exit blackbird_fini(void) { cx8802_unregister_driver(&cx8802_blackbird_driver); } diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index a5cc1c1fc2d..33be6369871 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1283,6 +1283,51 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_WINFAST_DTV2000H_J] = { + .name = "WinFast DTV2000 H rev. J", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00017300, + .gpio1 = 0x00008207, + .gpio2 = 0x00000000, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00018300, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00015702, + .gpio1 = 0x0000f207, + .gpio2 = 0x00015702, + .gpio3 = 0x02000000, + }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_GENIATECH_DVBS] = { .name = "Geniatech DVB-S", .tuner_type = TUNER_ABSENT, @@ -1908,7 +1953,8 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_DVB, - .vmux = 1, + .vmux = 0, + .gpio0 = 0x8080, } }, .mpeg = CX88_MPEG_DVB, }, @@ -2282,6 +2328,10 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x665e, .card = CX88_BOARD_WINFAST_DTV2000H, },{ + .subvendor = 0x107d, + .subdevice = 0x6f2b, + .card = CX88_BOARD_WINFAST_DTV2000H_J, + },{ .subvendor = 0x18ac, .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, @@ -3003,6 +3053,14 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ctl->demod = XC3028_FE_OREN538; break; + case CX88_BOARD_GENIATECH_X8000_MT: + /* FIXME: For this board, the xc3028 never recovers after being + powered down (the reset GPIO probably is not set properly). + We don't have access to the hardware so we cannot determine + which GPIO is used for xc3028, so just disable power xc3028 + power management for now */ + ctl->disable_power_mgmt = 1; + break; case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: @@ -3154,7 +3212,11 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: case CX88_BOARD_SATTRADE_ST4200: + cx_write(MO_GP0_IO, 0x8000); + msleep(100); cx_write(MO_SRST_IO, 0); + msleep(10); + cx_write(MO_GP0_IO, 0x8080); msleep(100); cx_write(MO_SRST_IO, 1); msleep(100); @@ -3377,20 +3439,20 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) The radio_type is sometimes missing, or set to UNSET but later code configures a tea5767. */ - v4l2_i2c_new_probed_subdev(&core->v4l2_dev, &core->i2c_adap, + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO)); if (has_demod) - v4l2_i2c_new_probed_subdev(&core->v4l2_dev, + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (core->board.tuner_addr == ADDR_UNSET) { - v4l2_i2c_new_probed_subdev(&core->v4l2_dev, + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, "tuner", "tuner", - has_demod ? tv_addrs + 4 : tv_addrs); + 0, has_demod ? tv_addrs + 4 : tv_addrs); } else { v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, - "tuner", "tuner", core->board.tuner_addr); + "tuner", "tuner", core->board.tuner_addr, NULL); } } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index c44e8760021..518bcfe18bc 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -424,17 +424,16 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; + cx_set(MO_GP0_IO, 0x6040); switch (voltage) { case SEC_VOLTAGE_13: - printk("LNB Voltage SEC_VOLTAGE_13\n"); - cx_write(MO_GP0_IO, 0x00006040); + cx_clear(MO_GP0_IO, 0x20); break; case SEC_VOLTAGE_18: - printk("LNB Voltage SEC_VOLTAGE_18\n"); - cx_write(MO_GP0_IO, 0x00006060); + cx_set(MO_GP0_IO, 0x20); break; case SEC_VOLTAGE_OFF: - printk("LNB Voltage SEC_VOLTAGE_off\n"); + cx_clear(MO_GP0_IO, 0x20); break; } @@ -499,8 +498,9 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = { }; static struct zl10353_config cx88_geniatech_x8000_mt = { - .demod_address = (0x1e >> 1), - .no_tuner = 1, + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, }; static struct s5h1411_config dvico_fusionhdtv7_config = { @@ -695,6 +695,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: case CX88_BOARD_HAUPPAUGE_HVR1300: @@ -1349,7 +1350,7 @@ static struct cx8802_driver cx8802_dvb_driver = { .advise_release = cx8802_dvb_advise_release, }; -static int dvb_init(void) +static int __init dvb_init(void) { printk(KERN_INFO "cx88/2: cx2388x dvb driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, @@ -1362,7 +1363,7 @@ static int dvb_init(void) return cx8802_register_driver(&cx8802_dvb_driver); } -static void dvb_fini(void) +static void __exit dvb_fini(void) { cx8802_unregister_driver(&cx8802_dvb_driver); } diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index d91f5c51206..78b3635178a 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -23,7 +23,7 @@ */ #include <linux/init.h> -#include <linux/delay.h> +#include <linux/hrtimer.h> #include <linux/input.h> #include <linux/pci.h> #include <linux/module.h> @@ -48,7 +48,7 @@ struct cx88_IR { /* poll external decoder */ int polling; - struct delayed_work work; + struct hrtimer timer; u32 gpio_addr; u32 last_gpio; u32 mask_keycode; @@ -144,19 +144,28 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } } -static void cx88_ir_work(struct work_struct *work) +static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) { - struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); + unsigned long missed; + struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); cx88_ir_handle_key(ir); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); + missed = hrtimer_forward_now(&ir->timer, + ktime_set(0, ir->polling * 1000000)); + if (missed > 1) + ir_dprintk("Missed ticks %ld\n", missed - 1); + + return HRTIMER_RESTART; } void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { - INIT_DELAYED_WORK(&ir->work, cx88_ir_work); - schedule_delayed_work(&ir->work, 0); + hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ir->timer.function = cx88_ir_work; + hrtimer_start(&ir->timer, + ktime_set(0, ir->polling * 1000000), + HRTIMER_MODE_REL); } if (ir->sampling) { core->pci_irqmask |= PCI_INT_IR_SMPINT; @@ -173,7 +182,7 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) } if (ir->polling) - cancel_delayed_work_sync(&ir->work); + hrtimer_cancel(&ir->timer); } /* ---------------------------------------------------------------------- */ @@ -182,7 +191,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) { struct cx88_IR *ir; struct input_dev *input_dev; - IR_KEYTAB_TYPE *ir_codes = NULL; + struct ir_scancode_table *ir_codes = NULL; int ir_type = IR_TYPE_OTHER; int err = -ENOMEM; @@ -198,14 +207,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_KWORLD_DVB_T_CX22702: - ir_codes = ir_codes_dntv_live_dvb_t; + ir_codes = &ir_codes_dntv_live_dvb_t_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 50; /* ms */ break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: - ir_codes = ir_codes_cinergy_1400; + ir_codes = &ir_codes_cinergy_1400_table; ir_type = IR_TYPE_PD; ir->sampling = 0xeb04; /* address */ break; @@ -220,13 +229,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = ir_codes_hauppauge_new; + ir_codes = &ir_codes_hauppauge_new_table; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_WINFAST_DTV1800H: - ir_codes = ir_codes_winfast; + ir_codes = &ir_codes_winfast_table; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; @@ -235,14 +245,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_WINFAST2000XP_EXPERT: case CX88_BOARD_WINFAST_DTV1000: case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: - ir_codes = ir_codes_winfast; + ir_codes = &ir_codes_winfast_table; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; ir->polling = 1; /* ms */ break; case CX88_BOARD_IODATA_GVBCTV7E: - ir_codes = ir_codes_iodata_bctv7e; + ir_codes = &ir_codes_iodata_bctv7e_table; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfd; ir->mask_keydown = 0x02; @@ -250,7 +260,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_PROLINK_PLAYTVPVR: case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x80; @@ -258,28 +268,28 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_PROLINK_PV_8000GT: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: - ir_codes = ir_codes_pixelview_new; + ir_codes = &ir_codes_pixelview_new_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x3f; ir->mask_keyup = 0x80; ir->polling = 1; /* ms */ break; case CX88_BOARD_KWORLD_LTV883: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 1; /* ms */ break; case CX88_BOARD_ADSTECH_DVB_T_PCI: - ir_codes = ir_codes_adstech_dvb_t_pci; + ir_codes = &ir_codes_adstech_dvb_t_pci_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0xbf; ir->mask_keyup = 0x40; ir->polling = 50; /* ms */ break; case CX88_BOARD_MSI_TVANYWHERE_MASTER: - ir_codes = ir_codes_msi_tvanywhere; + ir_codes = &ir_codes_msi_tvanywhere_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x40; @@ -287,40 +297,40 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_AVERTV_303: case CX88_BOARD_AVERTV_STUDIO_303: - ir_codes = ir_codes_avertv_303; + ir_codes = &ir_codes_avertv_303_table; ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0xfb; ir->mask_keydown = 0x02; ir->polling = 50; /* ms */ break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: - ir_codes = ir_codes_dntv_live_dvbt_pro; - ir_type = IR_TYPE_PD; - ir->sampling = 0xff00; /* address */ + ir_codes = &ir_codes_dntv_live_dvbt_pro_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_NORWOOD_MICRO: - ir_codes = ir_codes_norwood; + ir_codes = &ir_codes_norwood_table; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x0e; ir->mask_keyup = 0x80; ir->polling = 50; /* ms */ break; case CX88_BOARD_NPGTECH_REALTV_TOP10FM: - ir_codes = ir_codes_npgtech; - ir->gpio_addr = MO_GP0_IO; + ir_codes = &ir_codes_npgtech_table; + ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfa; - ir->polling = 50; /* ms */ + ir->polling = 50; /* ms */ break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: - ir_codes = ir_codes_pinnacle_pctv_hd; - ir_type = IR_TYPE_RC5; - ir->sampling = 1; + ir_codes = &ir_codes_pinnacle_pctv_hd_table; + ir_type = IR_TYPE_RC5; + ir->sampling = 1; break; case CX88_BOARD_POWERCOLOR_REAL_ANGEL: - ir_codes = ir_codes_powercolor_real_angel; - ir->gpio_addr = MO_GP2_IO; + ir_codes = &ir_codes_powercolor_real_angel_table; + ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0x7e; - ir->polling = 100; /* ms */ + ir->polling = 100; /* ms */ break; } diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index da4e3912cd3..de9ff0fc741 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -116,6 +116,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev, udelay(100); break; case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Enable MPEG parallel IO and video signal pins */ + cx_write(MO_PINMUX_IO, 0x88); + cx_write(TS_SOP_STAT, 0); + cx_write(TS_VALERR_CNTRL, 0); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: /* Enable MPEG parallel IO and video signal pins */ @@ -866,7 +870,7 @@ static struct pci_driver cx8802_pci_driver = { .remove = __devexit_p(cx8802_remove), }; -static int cx8802_init(void) +static int __init cx8802_init(void) { printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, @@ -879,7 +883,7 @@ static int cx8802_init(void) return pci_register_driver(&cx8802_pci_driver); } -static void cx8802_fini(void) +static void __exit cx8802_fini(void) { pci_unregister_driver(&cx8802_pci_driver); } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 2bb54c3ef5c..57e6b124109 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1881,14 +1881,14 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, if (core->board.audio_chip == V4L2_IDENT_WM8775) v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, - "wm8775", "wm8775", 0x36 >> 1); + "wm8775", "wm8775", 0x36 >> 1, NULL); if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { /* This probes for a tda9874 as is used on some Pixelview Ultra boards. */ - v4l2_i2c_new_probed_subdev_addr(&core->v4l2_dev, + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, - "tvaudio", "tvaudio", 0xb0 >> 1); + "tvaudio", "tvaudio", 0, I2C_ADDRS(0xb0 >> 1)); } switch (core->boardnr) { @@ -2113,7 +2113,7 @@ static struct pci_driver cx8800_pci_driver = { #endif }; -static int cx8800_init(void) +static int __init cx8800_init(void) { printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, @@ -2126,7 +2126,7 @@ static int cx8800_init(void) return pci_register_driver(&cx8800_pci_driver); } -static void cx8800_fini(void) +static void __exit cx8800_fini(void) { pci_unregister_driver(&cx8800_pci_driver); } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 9d83762163f..d5cea41f420 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -237,6 +237,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 +#define CX88_BOARD_WINFAST_DTV2000H_J 82 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index 0664d111085..ee43876adb0 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -748,14 +748,14 @@ static const struct file_operations dabusb_fops = .release = dabusb_release, }; -static char *dabusb_nodename(struct device *dev) +static char *dabusb_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } static struct usb_class_driver dabusb_class = { .name = "dabusb%d", - .nodename = dabusb_nodename, + .devnode = dabusb_devnode, .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile new file mode 100644 index 00000000000..1a8b8f3f182 --- /dev/null +++ b/drivers/media/video/davinci/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the davinci video device drivers. +# + +# VPIF +obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o + +#DM646x EVM Display driver +obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o +#DM646x EVM Capture driver +obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o + +# Capture: DM6446 and DM355 +obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/video/davinci/ccdc_hw_device.h new file mode 100644 index 00000000000..86b9b351896 --- /dev/null +++ b/drivers/media/video/davinci/ccdc_hw_device.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include <linux/videodev2.h> +#include <linux/device.h> +#include <media/davinci/vpfe_types.h> +#include <media/davinci/ccdc_types.h> + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* + * Pointer to function to set parameters. Used + * for implementing VPFE_S_CCDC_PARAMS + */ + int (*set_params) (void *params); + /* + * Pointer to function to get parameter. Used + * for implementing VPFE_G_CCDC_PARAMS + */ + int (*get_params) (void *params); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Query CCDC control IDs */ + int (*queryctrl)(struct v4l2_queryctrl *qctrl); + /* Set CCDC control */ + int (*set_control)(struct v4l2_control *ctrl); + /* Get CCDC control */ + int (*get_control)(struct v4l2_control *ctrl); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); + +#endif +#endif diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c new file mode 100644 index 00000000000..4629cabe3f2 --- /dev/null +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application include dm355_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <media/davinci/dm355_ccdc.h> +#include <media/davinci/vpss.h> +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .data_sz = CCDC_DATA_10BITS, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gama_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, +}; + + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +static enum vpfe_hw_if_type ccdc_if_type; +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN); + regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN); + regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN); + regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_hw_params_raw.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + dev = device; + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || + ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { + dev_dbg(dev, "Invalid value of data shift\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || + ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { + dev_dbg(dev, "Invalid value of median filter1\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || + ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { + dev_dbg(dev, "Invalid value of median filter2\n"); + return -EINVAL; + } + + if ((ccdcparam->med_filt_thres < 0) || + (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { + dev_dbg(dev, "Invalid value of median filter thresold\n"); + return -EINVAL; + } + + if (ccdcparam->data_sz < CCDC_DATA_16BITS || + ccdcparam->data_sz > CCDC_DATA_8BITS) { + dev_dbg(dev, "Invalid value of data size\n"); + return -EINVAL; + } + + if (ccdcparam->alaw.enable) { + if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + dev_dbg(dev, "Invalid value of ALAW\n"); + return -EINVAL; + } + } + + if (ccdcparam->blk_clamp.b_clamp_enable) { + if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || + ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { + dev_dbg(dev, "Invalid value of sample pixel\n"); + return -EINVAL; + } + if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || + ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { + dev_dbg(dev, "Invalid value of sample lines\n"); + return -EINVAL; + } + } + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying ccdc" + "params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + memcpy(&ccdc_hw_params_raw.config_params, + &ccdc_raw_params, + sizeof(ccdc_raw_params)); + return 0; + } + return -EINVAL; +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_init(void) +{ + printk(KERN_NOTICE "dm355_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm355_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm355_ccdc_init); +module_exit(dm355_ccdc_exit); diff --git a/drivers/media/video/davinci/dm355_ccdc_regs.h b/drivers/media/video/davinci/dm355_ccdc_regs.h new file mode 100644 index 00000000000..d6d2ef0533b --- /dev/null +++ b/drivers/media/video/davinci/dm355_ccdc_regs.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c new file mode 100644 index 00000000000..2f19a919f47 --- /dev/null +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters. This file is named DM644x so that other + * variants such DM6443 may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <media/davinci/dm644x_ccdc.h> +#include <media/davinci/vpss.h> +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, +}; + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; +static enum vpfe_hw_if_type ccdc_if_type; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->alaw.enable) { + if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || + (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + dev_dbg(dev, "\nInvalid data line select"); + return -1; + } + } + return 0; +} + +static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_virtaddr = NULL; + unsigned int *fpc_physaddr = NULL; + + memcpy(config_params, raw_params, sizeof(*raw_params)); + /* + * allocate memory for fault pixel table and copy the user + * values to the table + */ + if (!config_params->fault_pxl.enable) + return 0; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + fpc_virtaddr = (unsigned int *)phys_to_virt( + (unsigned long)fpc_physaddr); + /* + * Allocate memory for FPC table if current + * FPC table buffer is not big enough to + * accomodate FPC Number requested + */ + if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { + if (fpc_physaddr != NULL) { + free_pages((unsigned long)fpc_physaddr, + get_order + (config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + + /* Allocate memory for FPC table */ + fpc_virtaddr = + (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, + get_order(raw_params-> + fault_pxl.fp_num * + FP_NUM_BYTES)); + + if (fpc_virtaddr == NULL) { + dev_dbg(dev, + "\nUnable to allocate memory for FPC"); + return -EFAULT; + } + fpc_physaddr = + (unsigned int *)virt_to_phys((void *)fpc_virtaddr); + } + + /* Copy number of fault pixels and FPC table */ + config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; + if (copy_from_user(fpc_virtaddr, + (void __user *)raw_params->fault_pxl.fpc_table_addr, + config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { + dev_dbg(dev, "\n copy_from_user failed"); + return -EFAULT; + } + config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; + return 0; +} + +static int ccdc_close(struct device *dev) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + + if (fpc_physaddr != NULL) { + fpc_virtaddr = (unsigned int *) + phys_to_virt((unsigned long)fpc_physaddr); + free_pages((unsigned long)fpc_virtaddr, + get_order(config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying" + "ccdc params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + if (!ccdc_update_raw_params(&ccdc_raw_params)) + return 0; + } + return -EINVAL; +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 syn_mode; + + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte bondary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + ccdc_readregs(); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) +{ + u32 val; + + /* Initially disable FPC */ + val = CCDC_FPC_DISABLE; + regw(val, CCDC_FPC); + + if (!fpc->enable) + return; + + /* Configure Fault pixel if needed */ + regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); + dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n", + (fpc->fpc_table_addr)); + /* Write the FPC params with FPC disable */ + val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; + regw(val, CCDC_FPC); + + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + /* read the FPC register */ + val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; + regw(val, CCDC_FPC); + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Configure Fault Pixel Correction */ + ccdc_config_fpc(&config_params->fault_pxl); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + +#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE + /* enable video port */ + val = CCDC_ENABLE_VIDEO_PORT; +#else + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; +#endif + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_hw_params_raw.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm644x_ccdc_init(void) +{ + printk(KERN_NOTICE "dm644x_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm644x_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm644x_ccdc_init); +module_exit(dm644x_ccdc_exit); diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/video/davinci/dm644x_ccdc_regs.h new file mode 100644 index 00000000000..6e5d0532446 --- /dev/null +++ b/drivers/media/video/davinci/dm644x_ccdc_regs.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 + + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE (1 << 17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE (1 << 3) +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE (1 << 31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c new file mode 100644 index 00000000000..402ce43ef38 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,2124 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for Raw Bayer RGB capture + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for USERPTR IO + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <linux/io.h> +#include <media/davinci/vpfe_capture.h> +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 3; +static u32 bufsize = (720 * 576 * 2); + +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; + /* for storing mem maps for CCDC */ + int ccdc_addr_size; + void *__iomem ccdc_addr; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 3, + .numbuffers = 3, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .fmtdesc = { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit A-Law compr.", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb - 16bit", + .pixelformat = V4L2_PIX_FMT_SBGGR16, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit DPCM compr.", + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 3, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 4, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 5, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Y/CbCr 4:2:0 - Semi planar", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + .bpp = 1, + }, +}; + +/* + * vpfe_lookup_pix_format() + * lookup an entry in the vpfe pix format table based on pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + BUG_ON(!dev->hw_ops.open); + BUG_ON(!dev->hw_ops.enable); + BUG_ON(!dev->hw_ops.set_hw_if_params); + BUG_ON(!dev->hw_ops.configure); + BUG_ON(!dev->hw_ops.set_buftype); + BUG_ON(!dev->hw_ops.get_buftype); + BUG_ON(!dev->hw_ops.enum_pix); + BUG_ON(!dev->hw_ops.set_frame_format); + BUG_ON(!dev->hw_ops.get_frame_format); + BUG_ON(!dev->hw_ops.get_pixel_format); + BUG_ON(!dev->hw_ops.set_pixel_format); + BUG_ON(!dev->hw_ops.set_params); + BUG_ON(!dev->hw_ops.set_image_window); + BUG_ON(!dev->hw_ops.get_image_window); + BUG_ON(!dev->hw_ops.get_line_length); + BUG_ON(!dev->hw_ops.setfbaddr); + BUG_ON(!dev->hw_ops.getfid); + + mutex_lock(&ccdc_lock); + if (NULL == ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Proabably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -1; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -1; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -1; + goto unlock; + } + + ccdc_dev = dev; + dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr, + ccdc_cfg->ccdc_addr_size); +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) +{ + if (NULL == dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); + return; +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ccdc_dev->hw_ops.get_image_window(&image_win); + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = ccdc_dev->hw_ops.get_buftype(); + f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); + frm_fmt = ccdc_dev->hw_ops.get_frame_format(); + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + f->fmt.pix.field = V4L2_FIELD_NONE; + else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + const v4l2_std_id *std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & *std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + vpfe_dev->fmt.fmt.pix.width = vpfe_dev->crop.width; + vpfe_dev->fmt.fmt.pix.height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + } else { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + } + + /* if sub device supports g_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, g_fmt, &vpfe_dev->fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting g_fmt from sub device\n"); + return ret; + } + + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + if (!ret) { + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + } + return ret; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + + /* set default standard */ + vpfe_dev->std_index = 0; + + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (NULL == ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + if (NULL == fh) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + struct timeval timevalue; + + do_gettimeofday(&timevalue); + vpfe_dev->cur_frm->ts = timevalue; + vpfe_dev->cur_frm->state = VIDEOBUF_DONE; + vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&vpfe_dev->cur_frm->done); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + unsigned long addr; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + /* only for 6446 this will be applicable */ + if (NULL != ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + return IRQ_HANDLED; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) { + addr = + videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); + } + return IRQ_HANDLED; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(IRQ_VDINT1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + IRQF_DISABLED, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + /* Close the priority */ + v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (NULL == vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height =" + " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + cap->version = VPFE_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return ret; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + int temp_index; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (NULL != pix_fmt) { + temp_index = fmt->index; + *fmt = pix_fmt->fmtdesc; + fmt->index = temp_index; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + + if (NULL == pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe_dev); + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (NULL == pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -1; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index ; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found" + " for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input)); + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev_index, inp_index; + struct vpfe_route *route; + u32 input = 0, output = 0; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &inp_index, + index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_routing, input, output, 0); + + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + vpfe_dev->current_subdev = sdinfo; + vpfe_dev->current_input = index; + vpfe_dev->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + if (V4L2_MEMORY_USERPTR == req_buf->memory) { + /* we don't support user ptr IO */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs:" + " USERPTR IO not supported\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + NULL, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); + + if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop->bounds.width = crop->defrect.width = + vpfe_standards[vpfe_dev->std_index].width; + crop->bounds.height = crop->defrect.height = + vpfe_standards[vpfe_dev->std_index].height; + crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; + return 0; +} + +static int vpfe_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + + crop->c = vpfe_dev->crop; + return 0; +} + +static int vpfe_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (crop->c.top < 0 || crop->c.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundry */ + crop->c.width = ((crop->c.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((crop->c.left + crop->c.width > + vpfe_dev->std_info.active_pixels) || + (crop->c.top + crop->c.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&crop->c); + vpfe_dev->fmt.fmt.pix.width = crop->c.width; + vpfe_dev->fmt.fmt.pix.height = crop->c.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = crop->c; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +static long vpfe_param_handler(struct file *file, void *priv, + int cmd, void *param) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); + + if (vpfe_dev->started) { + /* only allowed if streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, "device already started\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + v4l2_warn(&vpfe_dev->v4l2_dev, + "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); + ret = ccdc_dev->hw_ops.set_params(param); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in setting parameters in CCDC\n"); + goto unlock_out; + } + if (vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Invalid image format at CCDC\n"); + goto unlock_out; + } + break; + default: + ret = -EINVAL; + } +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_crop = vpfe_g_crop, + .vidioc_s_crop = vpfe_s_crop, + .vidioc_default = vpfe_param_handler, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + + clk_disable(vpfe_cfg->vpssclk); + clk_put(vpfe_cfg->vpssclk); + clk_disable(vpfe_cfg->slaveclk); + clk_put(vpfe_cfg->slaveclk); + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master & slave clocks disabled\n"); +} + +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -ENOENT; + + vpfe_cfg->vpssclk = clk_get(vpfe_dev->pdev, "vpss_master"); + if (NULL == vpfe_cfg->vpssclk) { + v4l2_err(vpfe_dev->pdev->driver, "No clock defined for" + "vpss_master\n"); + return ret; + } + + if (clk_enable(vpfe_cfg->vpssclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss master clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master clock enabled\n"); + + vpfe_cfg->slaveclk = clk_get(vpfe_dev->pdev, "vpss_slave"); + if (NULL == vpfe_cfg->slaveclk) { + v4l2_err(vpfe_dev->pdev->driver, + "No clock defined for vpss slave\n"); + goto out; + } + + if (clk_enable(vpfe_cfg->slaveclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss slave clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, "vpfe vpss slave clock enabled\n"); + return 0; +out: + if (vpfe_cfg->vpssclk) + clk_put(vpfe_cfg->vpssclk); + if (vpfe_cfg->slaveclk) + clk_put(vpfe_cfg->slaveclk); + + return -1; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static __init int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret = -ENOMEM, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + vpfe_dev->pdev = &pdev->dev; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (NULL == vpfe_cfg->ccdc || + NULL == vpfe_cfg->card_name || + NULL == vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + mutex_lock(&ccdc_lock); + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); + if (NULL == ccdc_cfg) { + v4l2_err(pdev->dev.driver, + "Memory allocation failed for ccdc_cfg\n"); + goto probe_disable_clock; + } + + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get address base of CCDC */ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get register address map\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + + ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1; + if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size, + pdev->dev.driver->name)) { + v4l2_err(pdev->dev.driver, + "Failed request_mem_region for ccdc base\n"); + ret = -ENXIO; + goto probe_disable_clock; + } + ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start, + ccdc_cfg->ccdc_addr_size); + if (!ccdc_cfg->ccdc_addr) { + v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n"); + ret = -ENXIO; + goto probe_out_release_mem1; + } + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_out_unmap1; + } + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, + "Unable to alloc video device\n"); + goto probe_out_release_irq; + } + + /* Initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->minor = -1; + vfd->tvnorms = 0; + vfd->current_norm = V4L2_STD_PAL; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + vpfe_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* Initialize prio member of device object */ + v4l2_prio_init(&vpfe_dev->prio); + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%x\n", (int)&vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(vpfe_dev->video_dev, + VFL_TYPE_GRABBER, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(1); + vpfe_cfg = pdev->dev.platform_data; + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, + GFP_KERNEL); + if (NULL == vpfe_dev->sd) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + sdinfo->name, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->name); + goto probe_sd_out; + } + } + + /* set first sub device as current one */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_video_release: + if (vpfe_dev->video_dev->minor == -1) + video_device_release(vpfe_dev->video_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_out_unmap1: + iounmap(ccdc_cfg->ccdc_addr); +probe_out_release_mem1: + release_mem_region(res1->start, res1->end - res1->start + 1); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); + mutex_unlock(&ccdc_lock); + kfree(ccdc_cfg); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(vpfe_dev->video_dev); + mutex_lock(&ccdc_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + iounmap(ccdc_cfg->ccdc_addr); + mutex_unlock(&ccdc_lock); + vpfe_disable_clock(vpfe_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int +vpfe_suspend(struct device *dev) +{ + /* add suspend code here later */ + return -1; +} + +static int +vpfe_resume(struct device *dev) +{ + /* add resume code here later */ + return -1; +} + +static struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +static __init int vpfe_init(void) +{ + printk(KERN_NOTICE "vpfe_init\n"); + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/* + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c new file mode 100644 index 00000000000..3b8eac31eca --- /dev/null +++ b/drivers/media/video/davinci/vpif.c @@ -0,0 +1,296 @@ +/* + * vpif - DM646x Video Port Interface driver + * VPIF is a receiver and transmitter for video data. It has two channels(0, 1) + * that receiveing video byte stream and two channels(2, 3) for video output. + * The hardware supports SDTV, HDTV formats, raw data capture. + * Currently, the driver supports NTSC and PAL standards. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <mach/hardware.h> + +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver"); +MODULE_LICENSE("GPL"); + +#define VPIF_CH0_MAX_MODES (22) +#define VPIF_CH1_MAX_MODES (02) +#define VPIF_CH2_MAX_MODES (15) +#define VPIF_CH3_MAX_MODES (02) + +static resource_size_t res_len; +static struct resource *res; +spinlock_t vpif_lock; + +void __iomem *vpif_base; + +static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) +{ + if (val) + vpif_set_bit(reg, bit); + else + vpif_clr_bit(reg, bit); +} + +/* This structure is used to keep track of VPIF size register's offsets */ +struct vpif_registers { + u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl; + u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt; + u32 vanc1_size, width_mask, len_mask; + u8 max_modes; +}; + +static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = { + /* Channel0 */ + { + VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01, + VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL, + VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH0_MAX_MODES, + }, + /* Channel1 */ + { + VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01, + VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL, + VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH1_MAX_MODES, + }, + /* Channel2 */ + { + VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01, + VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL, + VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE, + VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH2_MAX_MODES + }, + /* Channel3 */ + { + VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01, + VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL, + VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE, + VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH3_MAX_MODES + }, +}; + +/* vpif_set_mode_info: + * This function is used to set horizontal and vertical config parameters + * As per the standard in the channel, configure the values of L1, L3, + * L5, L7 L9, L11 in VPIF Register , also write width and height + */ +static void vpif_set_mode_info(const struct vpif_channel_config_params *config, + u8 channel_id, u8 config_channel_id) +{ + u32 value; + + value = (config->eav2sav & vpifregs[config_channel_id].width_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->sav2eav & vpifregs[config_channel_id].width_mask); + regw(value, vpifregs[channel_id].h_cfg); + + value = (config->l1 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l3 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_00); + + value = (config->l5 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l7 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_01); + + value = (config->l9 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l11 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_02); + + value = (config->vsize & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg); +} + +/* config_vpif_params + * Function to set the parameters of a channel + * Mainly modifies the channel ciontrol register + * It sets frame format, yc mux mode + */ +static void config_vpif_params(struct vpif_params *vpifparams, + u8 channel_id, u8 found) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + u32 value, ch_nip, reg; + u8 start, end; + int i; + + start = channel_id; + end = channel_id + found; + + for (i = start; i < end; i++) { + reg = vpifregs[i].ch_ctrl; + if (channel_id < 2) + ch_nip = VPIF_CAPTURE_CH_NIP; + else + ch_nip = VPIF_DISPLAY_CH_NIP; + + vpif_wr_bit(reg, ch_nip, config->frm_fmt); + vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode); + vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT, + vpifparams->video_params.storage_mode); + + /* Set raster scanning SDR Format */ + vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT); + vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format); + + if (channel_id > 1) /* Set the Pixel enable bit */ + vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT); + else if (config->capture_format) { + /* Set the polarity of various pins */ + vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT, + vpifparams->iface.fid_pol); + vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT, + vpifparams->iface.vd_pol); + vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT, + vpifparams->iface.hd_pol); + + value = regr(reg); + /* Set data width */ + value &= ((~(unsigned int)(0x3)) << + VPIF_CH_DATA_WIDTH_BIT); + value |= ((vpifparams->params.data_sz) << + VPIF_CH_DATA_WIDTH_BIT); + regw(value, reg); + } + + /* Write the pitch in the driver */ + regw((vpifparams->video_params.hpitch), + vpifregs[i].line_offset); + } +} + +/* vpif_set_video_params + * This function is used to set video parameters in VPIF register + */ +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + int found = 1; + + vpif_set_mode_info(config, channel_id, channel_id); + if (!config->ycmux_mode) { + /* YC are on separate channels (HDTV formats) */ + vpif_set_mode_info(config, channel_id + 1, channel_id); + found = 2; + } + + config_vpif_params(vpifparams, channel_id, found); + + regw(0x80, VPIF_REQ_SIZE); + regw(0x01, VPIF_EMULATION_CTRL); + + return found; +} +EXPORT_SYMBOL(vpif_set_video_params); + +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id) +{ + u32 value; + + value = 0x3F8 & (vbiparams->hstart0); + value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16); + regw(value, vpifregs[channel_id].vanc0_strt); + + value = 0x3F8 & (vbiparams->hstart1); + value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16); + regw(value, vpifregs[channel_id].vanc1_strt); + + value = 0x3F8 & (vbiparams->hsize0); + value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16); + regw(value, vpifregs[channel_id].vanc0_size); + + value = 0x3F8 & (vbiparams->hsize1); + value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16); + regw(value, vpifregs[channel_id].vanc1_size); + +} +EXPORT_SYMBOL(vpif_set_vbi_display_params); + +int vpif_channel_getfid(u8 channel_id) +{ + return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK) + >> VPIF_CH_FID_SHIFT; +} +EXPORT_SYMBOL(vpif_channel_getfid); + +static int __init vpif_probe(struct platform_device *pdev) +{ + int status = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = res->end - res->start + 1; + + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpif_base = ioremap(res->start, res_len); + if (!vpif_base) { + status = -EBUSY; + goto fail; + } + + spin_lock_init(&vpif_lock); + dev_info(&pdev->dev, "vpif probe success\n"); + return 0; + +fail: + release_mem_region(res->start, res_len); + return status; +} + +static int vpif_remove(struct platform_device *pdev) +{ + iounmap(vpif_base); + release_mem_region(res->start, res_len); + return 0; +} + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpif_remove), + .probe = vpif_probe, +}; + +static void vpif_exit(void) +{ + platform_driver_unregister(&vpif_driver); +} + +static int __init vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} +subsys_initcall(vpif_init); +module_exit(vpif_exit); + diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h new file mode 100644 index 00000000000..188841b476e --- /dev/null +++ b/drivers/media/video/davinci/vpif.h @@ -0,0 +1,642 @@ +/* + * VPIF header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VPIF_H +#define VPIF_H + +#include <linux/io.h> +#include <linux/videodev2.h> +#include <mach/hardware.h> +#include <mach/dm646x.h> + +/* Maximum channel allowed */ +#define VPIF_NUM_CHANNELS (4) +#define VPIF_CAPTURE_NUM_CHANNELS (2) +#define VPIF_DISPLAY_NUM_CHANNELS (2) + +/* Macros to read/write registers */ +extern void __iomem *vpif_base; +extern spinlock_t vpif_lock; + +#define regr(reg) readl((reg) + vpif_base) +#define regw(value, reg) writel(value, (reg + vpif_base)) + +/* Register Addresss Offsets */ +#define VPIF_PID (0x0000) +#define VPIF_CH0_CTRL (0x0004) +#define VPIF_CH1_CTRL (0x0008) +#define VPIF_CH2_CTRL (0x000C) +#define VPIF_CH3_CTRL (0x0010) + +#define VPIF_INTEN (0x0020) +#define VPIF_INTEN_SET (0x0024) +#define VPIF_INTEN_CLR (0x0028) +#define VPIF_STATUS (0x002C) +#define VPIF_STATUS_CLR (0x0030) +#define VPIF_EMULATION_CTRL (0x0034) +#define VPIF_REQ_SIZE (0x0038) + +#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040) +#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044) +#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048) +#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c) +#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050) +#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054) +#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058) +#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c) +#define VPIF_CH0_SP_CFG (0x0060) +#define VPIF_CH0_IMG_ADD_OFST (0x0064) +#define VPIF_CH0_HANC_ADD_OFST (0x0068) +#define VPIF_CH0_H_CFG (0x006c) +#define VPIF_CH0_V_CFG_00 (0x0070) +#define VPIF_CH0_V_CFG_01 (0x0074) +#define VPIF_CH0_V_CFG_02 (0x0078) +#define VPIF_CH0_V_CFG_03 (0x007c) + +#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080) +#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084) +#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088) +#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c) +#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090) +#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094) +#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098) +#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c) +#define VPIF_CH1_SP_CFG (0x00a0) +#define VPIF_CH1_IMG_ADD_OFST (0x00a4) +#define VPIF_CH1_HANC_ADD_OFST (0x00a8) +#define VPIF_CH1_H_CFG (0x00ac) +#define VPIF_CH1_V_CFG_00 (0x00b0) +#define VPIF_CH1_V_CFG_01 (0x00b4) +#define VPIF_CH1_V_CFG_02 (0x00b8) +#define VPIF_CH1_V_CFG_03 (0x00bc) + +#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0) +#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4) +#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8) +#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc) +#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0) +#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4) +#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8) +#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc) +#define VPIF_CH2_SP_CFG (0x00e0) +#define VPIF_CH2_IMG_ADD_OFST (0x00e4) +#define VPIF_CH2_HANC_ADD_OFST (0x00e8) +#define VPIF_CH2_H_CFG (0x00ec) +#define VPIF_CH2_V_CFG_00 (0x00f0) +#define VPIF_CH2_V_CFG_01 (0x00f4) +#define VPIF_CH2_V_CFG_02 (0x00f8) +#define VPIF_CH2_V_CFG_03 (0x00fc) +#define VPIF_CH2_HANC0_STRT (0x0100) +#define VPIF_CH2_HANC0_SIZE (0x0104) +#define VPIF_CH2_HANC1_STRT (0x0108) +#define VPIF_CH2_HANC1_SIZE (0x010c) +#define VPIF_CH2_VANC0_STRT (0x0110) +#define VPIF_CH2_VANC0_SIZE (0x0114) +#define VPIF_CH2_VANC1_STRT (0x0118) +#define VPIF_CH2_VANC1_SIZE (0x011c) + +#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140) +#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144) +#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148) +#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c) +#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150) +#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154) +#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158) +#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c) +#define VPIF_CH3_SP_CFG (0x0160) +#define VPIF_CH3_IMG_ADD_OFST (0x0164) +#define VPIF_CH3_HANC_ADD_OFST (0x0168) +#define VPIF_CH3_H_CFG (0x016c) +#define VPIF_CH3_V_CFG_00 (0x0170) +#define VPIF_CH3_V_CFG_01 (0x0174) +#define VPIF_CH3_V_CFG_02 (0x0178) +#define VPIF_CH3_V_CFG_03 (0x017c) +#define VPIF_CH3_HANC0_STRT (0x0180) +#define VPIF_CH3_HANC0_SIZE (0x0184) +#define VPIF_CH3_HANC1_STRT (0x0188) +#define VPIF_CH3_HANC1_SIZE (0x018c) +#define VPIF_CH3_VANC0_STRT (0x0190) +#define VPIF_CH3_VANC0_SIZE (0x0194) +#define VPIF_CH3_VANC1_STRT (0x0198) +#define VPIF_CH3_VANC1_SIZE (0x019c) + +#define VPIF_IODFT_CTRL (0x01c0) + +/* Functions for bit Manipulation */ +static inline void vpif_set_bit(u32 reg, u32 bit) +{ + regw((regr(reg)) | (0x01 << bit), reg); +} + +static inline void vpif_clr_bit(u32 reg, u32 bit) +{ + regw(((regr(reg)) & ~(0x01 << bit)), reg); +} + +/* Macro for Generating mask */ +#ifdef GENERATE_MASK +#undef GENERATE_MASK +#endif + +#define GENERATE_MASK(bits, pos) \ + ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos) + +/* Bit positions in the channel control registers */ +#define VPIF_CH_DATA_MODE_BIT (2) +#define VPIF_CH_YC_MUX_BIT (3) +#define VPIF_CH_SDR_FMT_BIT (4) +#define VPIF_CH_HANC_EN_BIT (8) +#define VPIF_CH_VANC_EN_BIT (9) + +#define VPIF_CAPTURE_CH_NIP (10) +#define VPIF_DISPLAY_CH_NIP (11) + +#define VPIF_DISPLAY_PIX_EN_BIT (10) + +#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12) + +#define VPIF_CH_FID_POLARITY_BIT (15) +#define VPIF_CH_V_VALID_POLARITY_BIT (14) +#define VPIF_CH_H_VALID_POLARITY_BIT (13) +#define VPIF_CH_DATA_WIDTH_BIT (28) + +#define VPIF_CH_CLK_EDGE_CTRL_BIT (31) + +/* Mask various length */ +#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0) +#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_SHIFT (16) + +/* VPIF masks for registers */ +#define VPIF_REQ_SIZE_MASK (0x1ff) + +/* bit posotion of interrupt vpif_ch_intr register */ +#define VPIF_INTEN_FRAME_CH0 (0x00000001) +#define VPIF_INTEN_FRAME_CH1 (0x00000002) +#define VPIF_INTEN_FRAME_CH2 (0x00000004) +#define VPIF_INTEN_FRAME_CH3 (0x00000008) + +/* bit position of clock and channel enable in vpif_chn_ctrl register */ + +#define VPIF_CH0_CLK_EN (0x00000002) +#define VPIF_CH0_EN (0x00000001) +#define VPIF_CH1_CLK_EN (0x00000002) +#define VPIF_CH1_EN (0x00000001) +#define VPIF_CH2_CLK_EN (0x00000002) +#define VPIF_CH2_EN (0x00000001) +#define VPIF_CH3_CLK_EN (0x00000002) +#define VPIF_CH3_EN (0x00000001) +#define VPIF_CH_CLK_EN (0x00000002) +#define VPIF_CH_EN (0x00000001) + +#define VPIF_INT_TOP (0x00) +#define VPIF_INT_BOTTOM (0x01) +#define VPIF_INT_BOTH (0x02) + +#define VPIF_CH0_INT_CTRL_SHIFT (6) +#define VPIF_CH1_INT_CTRL_SHIFT (6) +#define VPIF_CH2_INT_CTRL_SHIFT (6) +#define VPIF_CH3_INT_CTRL_SHIFT (6) +#define VPIF_CH_INT_CTRL_SHIFT (6) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL)) + +#define VPIF_CH_FID_MASK (0x20) +#define VPIF_CH_FID_SHIFT (5) + +#define VPIF_NTSC_VBI_START_FIELD0 (1) +#define VPIF_NTSC_VBI_START_FIELD1 (263) +#define VPIF_PAL_VBI_START_FIELD0 (624) +#define VPIF_PAL_VBI_START_FIELD1 (311) + +#define VPIF_NTSC_HBI_START_FIELD0 (1) +#define VPIF_NTSC_HBI_START_FIELD1 (263) +#define VPIF_PAL_HBI_START_FIELD0 (624) +#define VPIF_PAL_HBI_START_FIELD1 (311) + +#define VPIF_NTSC_VBI_COUNT_FIELD0 (20) +#define VPIF_NTSC_VBI_COUNT_FIELD1 (19) +#define VPIF_PAL_VBI_COUNT_FIELD0 (24) +#define VPIF_PAL_VBI_COUNT_FIELD1 (25) + +#define VPIF_NTSC_HBI_COUNT_FIELD0 (263) +#define VPIF_NTSC_HBI_COUNT_FIELD1 (262) +#define VPIF_PAL_HBI_COUNT_FIELD0 (312) +#define VPIF_PAL_HBI_COUNT_FIELD1 (313) + +#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720) +#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720) +#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268) +#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280) + +#define VPIF_CH_VANC_EN (0x20) +#define VPIF_DMA_REQ_SIZE (0x080) +#define VPIF_EMULATION_DISABLE (0x01) + +extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS]; + +/* inline function to enable/disable channel0 */ +static inline void enable_channel0(int enable) +{ + if (enable) + regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL); + else + regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL); +} + +/* inline function to enable/disable channel1 */ +static inline void enable_channel1(int enable) +{ + if (enable) + regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL); + else + regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL); +} + +/* inline function to enable interrupt for channel0 */ +static inline void channel0_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable interrupt for channel1 */ +static inline void channel1_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA); +} + +static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + + regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +static inline void ch0_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC); +} + +static inline void ch0_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC); +} + +static inline void ch1_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC); +} + +static inline void ch1_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC); +} + +/* Inline function to enable raw vbi in the given channel */ +static inline void disable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +static inline void enable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +/* inline function to enable/disable channel2 */ +static inline void enable_channel2(int enable) +{ + if (enable) { + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL); + } else { + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL); + } +} + +/* inline function to enable/disable channel3 */ +static inline void enable_channel3(int enable) +{ + if (enable) { + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL); + } else { + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL); + } +} + +/* inline function to enable interrupt for channel2 */ +static inline void channel2_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable interrupt for channel3 */ +static inline void channel3_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable raw vbi data for channel2 */ +static inline void channel2_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH2_CTRL, mask); + else + vpif_clr_bit(VPIF_CH2_CTRL, mask); +} + +/* inline function to enable raw vbi data for channel3*/ +static inline void channel3_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH3_CTRL, mask); + else + vpif_clr_bit(VPIF_CH3_CTRL, mask); +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA); +} + +static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for vbi data */ +static inline void ch2_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC); +} + +static inline void ch3_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC); +} + +#define VPIF_MAX_NAME (30) + +/* This structure will store size parameters as per the mode selected by user */ +struct vpif_channel_config_params { + char name[VPIF_MAX_NAME]; /* Name of the mode */ + u16 width; /* Indicates width of the image */ + u16 height; /* Indicates height of the image */ + u8 fps; + u8 frm_fmt; /* Indicates whether this is interlaced + * or progressive format */ + u8 ycmux_mode; /* Indicates whether this mode requires + * single or two channels */ + u16 eav2sav; /* length of sav 2 eav */ + u16 sav2eav; /* length of sav 2 eav */ + u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */ + u16 vsize; /* Vertical size of the image */ + u8 capture_format; /* Indicates whether capture format + * is in BT or in CCD/CMOS */ + u8 vbi_supported; /* Indicates whether this mode + * supports capturing vbi or not */ + u8 hd_sd; + v4l2_std_id stdid; +}; + +struct vpif_video_params; +struct vpif_params; +struct vpif_vbi_params; + +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id); +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id); +int vpif_channel_getfid(u8 channel_id); + +enum data_size { + _8BITS = 0, + _10BITS, + _12BITS, +}; + +/* Structure for vpif parameters for raw vbi data */ +struct vpif_vbi_params { + __u32 hstart0; /* Horizontal start of raw vbi data for first field */ + __u32 vstart0; /* Vertical start of raw vbi data for first field */ + __u32 hsize0; /* Horizontal size of raw vbi data for first field */ + __u32 vsize0; /* Vertical size of raw vbi data for first field */ + __u32 hstart1; /* Horizontal start of raw vbi data for second field */ + __u32 vstart1; /* Vertical start of raw vbi data for second field */ + __u32 hsize1; /* Horizontal size of raw vbi data for second field */ + __u32 vsize1; /* Vertical size of raw vbi data for second field */ +}; + +/* structure for vpif parameters */ +struct vpif_video_params { + __u8 storage_mode; /* Indicates field or frame mode */ + unsigned long hpitch; + v4l2_std_id stdid; +}; + +struct vpif_params { + struct vpif_interface iface; + struct vpif_video_params video_params; + struct vpif_channel_config_params std_info; + union param { + struct vpif_vbi_params vbi_params; + enum data_size data_sz; + } params; +}; + +#endif /* End of #ifndef VPIF_H */ + diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c new file mode 100644 index 00000000000..d947ee5e4eb --- /dev/null +++ b/drivers/media/video/davinci/vpif_capture.c @@ -0,0 +1,2168 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO : add support for VBI & HBI data service + * add static buffer allocation + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/string.h> +#include <linux/videodev2.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/version.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include "vpif_capture.h" +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver"); +MODULE_LICENSE("GPL"); + +#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) +#define vpif_dbg(level, debug, fmt, arg...) \ + v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) + +static int debug = 1; +static u32 ch0_numbuffers = 3; +static u32 ch1_numbuffers = 3; +static u32 ch0_bufsize = 1920 * 1080 * 2; +static u32 ch1_bufsize = 720 * 576 * 2; + +module_param(debug, int, 0644); +module_param(ch0_numbuffers, uint, S_IRUGO); +module_param(ch1_numbuffers, uint, S_IRUGO); +module_param(ch0_bufsize, uint, S_IRUGO); +module_param(ch1_bufsize, uint, S_IRUGO); + +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)"); +MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)"); +MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)"); +MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)"); + +static struct vpif_config_params config_params = { + .min_numbuffers = 3, + .numbuffers[0] = 3, + .numbuffers[1] = 3, + .min_bufsize[0] = 720 * 480 * 2, + .min_bufsize[1] = 720 * 480 * 2, + .channel_bufsize[0] = 1920 * 1080 * 2, + .channel_bufsize[1] = 720 * 576 * 2, +}; + +/* global variables */ +static struct vpif_device vpif_obj = { {NULL} }; +static struct device *vpif_dev; + +/** + * ch_params: video standard configuration parameters for vpif + */ +static const struct vpif_channel_config_params ch_params[] = { + { + "NTSC_M", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, + 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, + }, + { + "PAL_BDGHIK", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, + 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, + }, +}; + +/** + * vpif_uservirt_to_phys : translate user/virtual address to phy address + * @virtp: user/virtual address + * + * This inline function is used to convert user space virtual address to + * physical address. + */ +static inline u32 vpif_uservirt_to_phys(u32 virtp) +{ + unsigned long physp = 0; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) + physp = virt_to_phys((void *)virtp); + else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) + /** + * this will catch, kernel-allocated, mmaped-to-usermode + * addresses + */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, + virtp, nr_pages, 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + else { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + return physp; +} + +/** + * buffer_prepare : callback function for buffer prepare + * @q : buffer queue ptr + * @vb: ptr to video buffer + * @field: field info + * + * This is the callback function for buffer prepare when videobuf_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + unsigned long addr; + + + vpif_dbg(2, debug, "vpif_buffer_prepare\n"); + + common = &ch->common[VPIF_VIDEO_INDEX]; + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = common->width; + vb->height = common->height; + vb->size = vb->width * vb->height; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + /** + * if user pointer memory mechanism is used, get the physical + * address of the buffer + */ + if (V4L2_MEMORY_USERPTR == common->memory) { + if (0 == vb->baddr) { + vpif_dbg(1, debug, "buffer address is 0\n"); + return -EINVAL; + + } + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!IS_ALIGNED(vb->boff, 8)) + goto exit; + } + + addr = vb->boff; + if (q->streaming) { + if (!IS_ALIGNED((addr + common->ytop_off), 8) || + !IS_ALIGNED((addr + common->ybtm_off), 8) || + !IS_ALIGNED((addr + common->ctop_off), 8) || + !IS_ALIGNED((addr + common->cbtm_off), 8)) + goto exit; + } + return 0; +exit: + vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n"); + return -EINVAL; +} + +/** + * vpif_buffer_setup : Callback function for buffer setup. + * @q: buffer queue ptr + * @count: number of buffers + * @size: size of the buffer + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size + */ +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_buffer_setup\n"); + + /* If memory type is not mmap, return */ + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + /* Calculate the size of the buffer */ + *size = config_params.channel_bufsize[ch->channel_id]; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + return 0; +} + +/** + * vpif_buffer_queue : Callback function to add buffer to DMA queue + * @q: ptr to videobuf_queue + * @vb: ptr to videobuf_buffer + */ +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &common->dma_queue); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +/** + * vpif_buffer_release : Callback function to free buffer + * @q: buffer queue ptr + * @vb: ptr to video buffer + * + * This function is called from the videobuf layer to free memory + * allocated to the buffers + */ +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpif_buffer_setup, + .buf_prepare = vpif_buffer_prepare, + .buf_queue = vpif_buffer_queue, + .buf_release = vpif_buffer_release, +}; + +static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = + { {1, 1} }; + +/** + * vpif_process_buffer_complete: process a completed buffer + * @common: ptr to common channel object + * + * This function time stamp the buffer and mark it as DONE. It also + * wake up any process waiting on the QUEUE and set the next buffer + * as current + */ +static void vpif_process_buffer_complete(struct common_obj *common) +{ + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); + /* Make curFrm pointing to nextFrm */ + common->cur_frm = common->next_frm; +} + +/** + * vpif_schedule_next_buffer: set next buffer address for capture + * @common : ptr to common channel object + * + * This function will get next buffer from the dma queue and + * set the buffer address in the vpif register for capture. + * the buffer is marked active + */ +static void vpif_schedule_next_buffer(struct common_obj *common) +{ + unsigned long addr = 0; + + common->next_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&common->next_frm->queue); + common->next_frm->state = VIDEOBUF_ACTIVE; + if (V4L2_MEMORY_USERPTR == common->memory) + addr = common->next_frm->boff; + else + addr = videobuf_to_dma_contig(common->next_frm); + + /* Set top and bottom field addresses in VPIF registers */ + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); +} + +/** + * vpif_channel_isr : ISR handler for vpif capture + * @irq: irq number + * @dev_id: dev_id ptr + * + * It changes status of the captured buffer, takes next buffer from the queue + * and sets its address in VPIF registers + */ +static irqreturn_t vpif_channel_isr(int irq, void *dev_id) +{ + struct vpif_device *dev = &vpif_obj; + struct common_obj *common; + struct channel_obj *ch; + enum v4l2_field field; + int channel_id = 0; + int fid = -1, i; + + channel_id = *(int *)(dev_id); + ch = dev->dev[channel_id]; + + field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; + + for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) { + common = &ch->common[i]; + /* skip If streaming is not started in this channel */ + if (0 == common->started) + continue; + + /* Check the field format */ + if (1 == ch->vpifparams.std_info.frm_fmt) { + /* Progressive mode */ + if (list_empty(&common->dma_queue)) + continue; + + if (!channel_first_int[i][channel_id]) + vpif_process_buffer_complete(common); + + channel_first_int[i][channel_id] = 0; + + vpif_schedule_next_buffer(common); + + + channel_first_int[i][channel_id] = 0; + } else { + /** + * Interlaced mode. If it is first interrupt, ignore + * it + */ + if (channel_first_int[i][channel_id]) { + channel_first_int[i][channel_id] = 0; + continue; + } + if (0 == i) { + ch->field_id ^= 1; + /* Get field id from VPIF registers */ + fid = vpif_channel_getfid(ch->channel_id); + if (fid != ch->field_id) { + /** + * If field id does not match stored + * field id, make them in sync + */ + if (0 == fid) + ch->field_id = fid; + return IRQ_HANDLED; + } + } + /* device field id and local field id are in sync */ + if (0 == fid) { + /* this is even field */ + if (common->cur_frm == common->next_frm) + continue; + + /* mark the current buffer as done */ + vpif_process_buffer_complete(common); + } else if (1 == fid) { + /* odd field */ + if (list_empty(&common->dma_queue) || + (common->cur_frm != common->next_frm)) + continue; + + vpif_schedule_next_buffer(common); + } + } + } + return IRQ_HANDLED; +} + +/** + * vpif_update_std_info() - update standard related info + * @ch: ptr to channel object + * + * For a given standard selected by application, update values + * in the device data structures + */ +static int vpif_update_std_info(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_params *vpifparams = &ch->vpifparams; + const struct vpif_channel_config_params *config; + struct vpif_channel_config_params *std_info; + struct video_obj *vid_ch = &ch->video; + int index; + + vpif_dbg(2, debug, "vpif_update_std_info\n"); + + std_info = &vpifparams->std_info; + + for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + config = &ch_params[index]; + if (config->stdid & vid_ch->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } + + /* standard not found */ + if (index == ARRAY_SIZE(ch_params)) + return -EINVAL; + + common->fmt.fmt.pix.width = std_info->width; + common->width = std_info->width; + common->fmt.fmt.pix.height = std_info->height; + common->height = std_info->height; + common->fmt.fmt.pix.bytesperline = std_info->width; + vpifparams->video_params.hpitch = std_info->width; + vpifparams->video_params.storage_mode = std_info->frm_fmt; + return 0; +} + +/** + * vpif_calculate_offsets : This function calculates buffers offsets + * @ch : ptr to channel object + * + * This function calculates buffer offsets for Y and C in the top and + * bottom field + */ +static void vpif_calculate_offsets(struct channel_obj *ch) +{ + unsigned int hpitch, vpitch, sizeimage; + struct video_obj *vid_ch = &(ch->video); + struct vpif_params *vpifparams = &ch->vpifparams; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + enum v4l2_field field = common->fmt.fmt.pix.field; + + vpif_dbg(2, debug, "vpif_calculate_offsets\n"); + + if (V4L2_FIELD_ANY == field) { + if (vpifparams->std_info.frm_fmt) + vid_ch->buf_field = V4L2_FIELD_NONE; + else + vid_ch->buf_field = V4L2_FIELD_INTERLACED; + } else + vid_ch->buf_field = common->fmt.fmt.pix.field; + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + hpitch = common->fmt.fmt.pix.bytesperline; + vpitch = sizeimage / (hpitch * 2); + + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ytop_off = 0; + common->ybtm_off = hpitch; + common->ctop_off = sizeimage / 2; + common->cbtm_off = sizeimage / 2 + hpitch; + } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ytop_off = 0; + common->ybtm_off = sizeimage / 4; + common->ctop_off = sizeimage / 2; + common->cbtm_off = common->ctop_off + sizeimage / 4; + } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ybtm_off = 0; + common->ytop_off = sizeimage / 4; + common->cbtm_off = sizeimage / 2; + common->ctop_off = common->cbtm_off + sizeimage / 4; + } + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) + vpifparams->video_params.storage_mode = 1; + else + vpifparams->video_params.storage_mode = 0; + + if (1 == vpifparams->std_info.frm_fmt) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + else { + if ((field == V4L2_FIELD_ANY) + || (field == V4L2_FIELD_INTERLACED)) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline * 2; + else + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } + + ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid; +} + +/** + * vpif_config_format: configure default frame format in the device + * ch : ptr to channel object + */ +static void vpif_config_format(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_config_format\n"); + + common->fmt.fmt.pix.field = V4L2_FIELD_ANY; + if (config_params.numbuffers[ch->channel_id] == 0) + common->memory = V4L2_MEMORY_USERPTR; + else + common->memory = V4L2_MEMORY_MMAP; + + common->fmt.fmt.pix.sizeimage + = config_params.channel_bufsize[ch->channel_id]; + + if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + else + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +} + +/** + * vpif_get_default_field() - Get default field type based on interface + * @vpif_params - ptr to vpif params + */ +static inline enum v4l2_field vpif_get_default_field( + struct vpif_interface *iface) +{ + return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE : + V4L2_FIELD_INTERLACED; +} + +/** + * vpif_check_format() - check given pixel format for compatibility + * @ch - channel ptr + * @pixfmt - Given pixel format + * @update - update the values as per hardware requirement + * + * Check the application pixel format for S_FMT and update the input + * values as per hardware limits for TRY_FMT. The default pixel and + * field format is selected based on interface type. + */ +static int vpif_check_format(struct channel_obj *ch, + struct v4l2_pix_format *pixfmt, + int update) +{ + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + struct vpif_params *vpif_params = &ch->vpifparams; + enum v4l2_field field = pixfmt->field; + u32 sizeimage, hpitch, vpitch; + int ret = -EINVAL; + + vpif_dbg(2, debug, "vpif_check_format\n"); + /** + * first check for the pixel format. If if_type is Raw bayer, + * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only + * V4L2_PIX_FMT_YUV422P is supported + */ + if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { + if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) { + if (!update) { + vpif_dbg(2, debug, "invalid pix format\n"); + goto exit; + } + pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; + } + } else { + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) { + if (!update) { + vpif_dbg(2, debug, "invalid pixel format\n"); + goto exit; + } + pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; + } + } + + if (!(VPIF_VALID_FIELD(field))) { + if (!update) { + vpif_dbg(2, debug, "invalid field format\n"); + goto exit; + } + /** + * By default use FIELD_NONE for RAW Bayer capture + * and FIELD_INTERLACED for other interfaces + */ + field = vpif_get_default_field(&vpif_params->iface); + } else if (field == V4L2_FIELD_ANY) + /* unsupported field. Use default */ + field = vpif_get_default_field(&vpif_params->iface); + + /* validate the hpitch */ + hpitch = pixfmt->bytesperline; + if (hpitch < vpif_params->std_info.width) { + if (!update) { + vpif_dbg(2, debug, "invalid hpitch\n"); + goto exit; + } + hpitch = vpif_params->std_info.width; + } + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + vpitch = sizeimage / (hpitch * 2); + + /* validate the vpitch */ + if (vpitch < vpif_params->std_info.height) { + if (!update) { + vpif_dbg(2, debug, "Invalid vpitch\n"); + goto exit; + } + vpitch = vpif_params->std_info.height; + } + + /* Check for 8 byte alignment */ + if (!ALIGN(hpitch, 8)) { + if (!update) { + vpif_dbg(2, debug, "invalid pitch alignment\n"); + goto exit; + } + /* adjust to next 8 byte boundary */ + hpitch = (((hpitch + 7) / 8) * 8); + } + /* if update is set, modify the bytesperline and sizeimage */ + if (update) { + pixfmt->bytesperline = hpitch; + pixfmt->sizeimage = hpitch * vpitch * 2; + } + /** + * Image width and height is always based on current standard width and + * height + */ + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + return 0; +exit: + return ret; +} + +/** + * vpif_config_addr() - function to configure buffer address in vpif + * @ch - channel ptr + * @muxmode - channel mux mode + */ +static void vpif_config_addr(struct channel_obj *ch, int muxmode) +{ + struct common_obj *common; + + vpif_dbg(2, debug, "vpif_config_addr\n"); + + common = &(ch->common[VPIF_VIDEO_INDEX]); + + if (VPIF_CHANNEL1_VIDEO == ch->channel_id) + common->set_addr = ch1_set_videobuf_addr; + else if (2 == muxmode) + common->set_addr = ch0_set_videobuf_addr_yc_nmux; + else + common->set_addr = ch0_set_videobuf_addr; +} + +/** + * vpfe_mmap : It is used to map kernel space buffers into user spaces + * @filep: file pointer + * @vma: ptr to vm_area_struct + */ +static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) +{ + /* Get the channel object and file handle object */ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + + vpif_dbg(2, debug, "vpif_mmap\n"); + + return videobuf_mmap_mapper(&common->buffer_queue, vma); +} + +/** + * vpif_poll: It is used for select/poll system call + * @filep: file pointer + * @wait: poll table to wait + */ +static unsigned int vpif_poll(struct file *filep, poll_table * wait) +{ + int err = 0; + struct vpif_fh *fh = filep->private_data; + struct channel_obj *channel = fh->channel; + struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]); + + vpif_dbg(2, debug, "vpif_poll\n"); + + if (common->started) + err = videobuf_poll_stream(filep, &common->buffer_queue, wait); + + return 0; +} + +/** + * vpif_open : vpif open handler + * @filep: file ptr + * + * It creates object of file handle structure and stores it in private_data + * member of filepointer + */ +static int vpif_open(struct file *filep) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(filep); + struct common_obj *common; + struct video_obj *vid_ch; + struct channel_obj *ch; + struct vpif_fh *fh; + int i, ret = 0; + + vpif_dbg(2, debug, "vpif_open\n"); + + ch = video_get_drvdata(vdev); + + vid_ch = &ch->video; + common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (NULL == ch->curr_subdev_info) { + /** + * search through the sub device to see a registered + * sub device and make it as current sub device + */ + for (i = 0; i < config->subdev_count; i++) { + if (vpif_obj.sd[i]) { + /* the sub device is registered */ + ch->curr_subdev_info = &config->subdev_info[i]; + /* make first input as the current input */ + vid_ch->input_idx = 0; + break; + } + } + if (i == config->subdev_count) { + vpif_err("No sub device registered\n"); + ret = -ENOENT; + goto exit; + } + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); + if (NULL == fh) { + vpif_err("unable to allocate memory for file handle object\n"); + ret = -ENOMEM; + goto exit; + } + + /* store pointer to fh in private_data member of filep */ + filep->private_data = fh; + fh->channel = ch; + fh->initialized = 0; + /* If decoder is not initialized. initialize it */ + if (!ch->initialized) { + fh->initialized = 1; + ch->initialized = 1; + memset(&(ch->vpifparams), 0, sizeof(struct vpif_params)); + } + /* Increment channel usrs counter */ + ch->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed[VPIF_VIDEO_INDEX] = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&ch->prio, &fh->prio); +exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_release : function to clean up file close + * @filep: file pointer + * + * This function deletes buffer queue, frees the buffers and the vpfe file + * handle + */ +static int vpif_release(struct file *filep) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + vpif_dbg(2, debug, "vpif_release\n"); + + common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* if this instance is doing IO */ + if (fh->io_allowed[VPIF_VIDEO_INDEX]) { + /* Reset io_usrs member of channel object */ + common->io_usrs = 0; + /* Disable channel as per its device type and channel id */ + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { + enable_channel0(0); + channel0_intr_enable(0); + } + if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel1(0); + channel1_intr_enable(0); + } + common->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); + } + + /* Decrement channel usrs counter */ + ch->usrs--; + + /* unlock mutex on channel object */ + mutex_unlock(&common->lock); + + /* Close the priority */ + v4l2_prio_close(&ch->prio, &fh->prio); + + if (fh->initialized) + ch->initialized = 0; + + filep->private_data = NULL; + kfree(fh); + return 0; +} + +/** + * vpif_reqbufs() - request buffer handler + * @file: file ptr + * @priv: file handle + * @reqbuf: request buffer structure ptr + */ +static int vpif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + u8 index = 0; + int ret = 0; + + vpif_dbg(2, debug, "vpif_reqbufs\n"); + + /** + * This file handle has not initialized the channel, + * It is not allowed to do settings + */ + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) + || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type) + return -EINVAL; + + index = VPIF_VIDEO_INDEX; + + common = &ch->common[index]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (0 != common->io_usrs) { + ret = -EBUSY; + goto reqbuf_exit; + } + + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&common->buffer_queue, + &video_qops, NULL, + &common->irqlock, + reqbuf->type, + common->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), fh); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed[index] = 1; + /* Increment io usrs member of channel object to 1 */ + common->io_usrs = 1; + /* Store type of memory requested in channel object */ + common->memory = reqbuf->memory; + INIT_LIST_HEAD(&common->dma_queue); + + /* Allocate buffers */ + ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); + +reqbuf_exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_querybuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_querybuf\n"); + + if (common->fmt.type != buf->type) + return -EINVAL; + + if (common->memory != V4L2_MEMORY_MMAP) { + vpif_dbg(1, debug, "Invalid memory\n"); + return -EINVAL; + } + + return videobuf_querybuf(&common->buffer_queue, buf); +} + +/** + * vpif_qbuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_buffer tbuf = *buf; + struct videobuf_buffer *buf1; + unsigned long addr = 0; + unsigned long flags; + int ret = 0; + + vpif_dbg(2, debug, "vpif_qbuf\n"); + + if (common->fmt.type != tbuf.type) { + vpif_err("invalid buffer type\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh io not allowed \n"); + return -EACCES; + } + + if (!(list_empty(&common->dma_queue)) || + (common->cur_frm != common->next_frm) || + !common->started || + (common->started && (0 == ch->field_id))) + return videobuf_qbuf(&common->buffer_queue, buf); + + /* bufferqueue is empty store buffer address in VPIF registers */ + mutex_lock(&common->buffer_queue.vb_lock); + buf1 = common->buffer_queue.bufs[tbuf.index]; + + if ((buf1->state == VIDEOBUF_QUEUED) || + (buf1->state == VIDEOBUF_ACTIVE)) { + vpif_err("invalid state\n"); + goto qbuf_exit; + } + + switch (buf1->memory) { + case V4L2_MEMORY_MMAP: + if (buf1->baddr == 0) + goto qbuf_exit; + break; + + case V4L2_MEMORY_USERPTR: + if (tbuf.length < buf1->bsize) + goto qbuf_exit; + + if ((VIDEOBUF_NEEDS_INIT != buf1->state) + && (buf1->baddr != tbuf.m.userptr)) + vpif_buffer_release(&common->buffer_queue, buf1); + buf1->baddr = tbuf.m.userptr; + break; + + default: + goto qbuf_exit; + } + + local_irq_save(flags); + ret = vpif_buffer_prepare(&common->buffer_queue, buf1, + common->buffer_queue.field); + if (ret < 0) { + local_irq_restore(flags); + goto qbuf_exit; + } + + buf1->state = VIDEOBUF_ACTIVE; + + if (V4L2_MEMORY_USERPTR == common->memory) + addr = buf1->boff; + else + addr = videobuf_to_dma_contig(buf1); + + common->next_frm = buf1; + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); + + local_irq_restore(flags); + list_add_tail(&buf1->stream, &common->buffer_queue.stream); + mutex_unlock(&common->buffer_queue.vb_lock); + return 0; + +qbuf_exit: + mutex_unlock(&common->buffer_queue.vb_lock); + return -EINVAL; +} + +/** + * vpif_dqbuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_dqbuf\n"); + + return videobuf_dqbuf(&common->buffer_queue, buf, + file->f_flags & O_NONBLOCK); +} + +/** + * vpif_streamon() - streamon handler + * @file: file ptr + * @priv: file handle + * @buftype: v4l2 buffer type + */ +static int vpif_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; + struct vpif_params *vpif; + unsigned long addr = 0; + int ret = 0; + + vpif_dbg(2, debug, "vpif_streamon\n"); + + vpif = &ch->vpifparams; + + if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + vpif_dbg(1, debug, "buffer type not supported\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_dbg(1, debug, "io not allowed\n"); + return -EACCES; + } + + /* If Streaming is already started, return error */ + if (common->started) { + vpif_dbg(1, debug, "channel->started\n"); + return -EBUSY; + } + + if ((ch->channel_id == VPIF_CHANNEL0_VIDEO && + oth_ch->common[VPIF_VIDEO_INDEX].started && + vpif->std_info.ycmux_mode == 0) || + ((ch->channel_id == VPIF_CHANNEL1_VIDEO) && + (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { + vpif_dbg(1, debug, "other channel is being used\n"); + return -EBUSY; + } + + ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0); + if (ret) + return ret; + + /* Enable streamon on the sub device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + vpif_dbg(1, debug, "stream on failed in subdev\n"); + return ret; + } + + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); + if (ret) { + vpif_dbg(1, debug, "videobuf_streamon\n"); + return ret; + } + + if (mutex_lock_interruptible(&common->lock)) { + ret = -ERESTARTSYS; + goto streamoff_exit; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&common->dma_queue)) { + vpif_dbg(1, debug, "buffer queue is empty\n"); + ret = -EIO; + goto exit; + } + + /* Get the next frame from the buffer queue */ + common->cur_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + common->next_frm = common->cur_frm; + + /* Remove buffer from the buffer queue */ + list_del(&common->cur_frm->queue); + /* Mark state of the current frame to active */ + common->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + ch->field_id = 0; + common->started = 1; + + if (V4L2_MEMORY_USERPTR == common->memory) + addr = common->cur_frm->boff; + else + addr = videobuf_to_dma_contig(common->cur_frm); + + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + + if ((vpif->std_info.frm_fmt && + ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) && + (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) || + (!vpif->std_info.frm_fmt && + (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { + vpif_dbg(1, debug, "conflict in field format and std format\n"); + ret = -EINVAL; + goto exit; + } + + /* configure 1 or 2 channel mode */ + ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode); + + if (ret < 0) { + vpif_dbg(1, debug, "can't set vpif channel mode\n"); + goto exit; + } + + /* Call vpif_set_params function to set the parameters and addresses */ + ret = vpif_set_video_params(vpif, ch->channel_id); + + if (ret < 0) { + vpif_dbg(1, debug, "can't set video params\n"); + goto exit; + } + + common->started = ret; + vpif_config_addr(ch, ret); + + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); + + /** + * Set interrupt for both the fields in VPIF Register enable channel in + * VPIF register + */ + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) { + channel0_intr_assert(); + channel0_intr_enable(1); + enable_channel0(1); + } + if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || + (common->started == 2)) { + channel1_intr_assert(); + channel1_intr_enable(1); + enable_channel1(1); + } + channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; + mutex_unlock(&common->lock); + return ret; + +exit: + mutex_unlock(&common->lock); +streamoff_exit: + ret = videobuf_streamoff(&common->buffer_queue); + return ret; +} + +/** + * vpif_streamoff() - streamoff handler + * @file: file ptr + * @priv: file handle + * @buftype: v4l2 buffer type + */ +static int vpif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret; + + vpif_dbg(2, debug, "vpif_streamoff\n"); + + if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + vpif_dbg(1, debug, "buffer type not supported\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_dbg(1, debug, "io not allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!common->started) { + vpif_dbg(1, debug, "channel->started\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* disable channel */ + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { + enable_channel0(0); + channel0_intr_enable(0); + } else { + enable_channel1(0); + channel1_intr_enable(0); + } + + common->started = 0; + + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + vpif_dbg(1, debug, "stream off failed in subdev\n"); + + mutex_unlock(&common->lock); + + return videobuf_streamoff(&common->buffer_queue); +} + +/** + * vpif_map_sub_device_to_input() - Maps sub device to input + * @ch - ptr to channel + * @config - ptr to capture configuration + * @input_index - Given input index from application + * @sub_device_index - index into sd table + * + * lookup the sub device information for a given input index. + * we report all the inputs to application. inputs table also + * has sub device name for the each input + */ +static struct vpif_subdev_info *vpif_map_sub_device_to_input( + struct channel_obj *ch, + struct vpif_capture_config *vpif_cfg, + int input_index, + int *sub_device_index) +{ + struct vpif_capture_chan_config *chan_cfg; + struct vpif_subdev_info *subdev_info = NULL; + const char *subdev_name = NULL; + int i; + + vpif_dbg(2, debug, "vpif_map_sub_device_to_input\n"); + + chan_cfg = &vpif_cfg->chan_config[ch->channel_id]; + + /** + * search through the inputs to find the sub device supporting + * the input + */ + for (i = 0; i < chan_cfg->input_count; i++) { + /* For each sub device, loop through input */ + if (i == input_index) { + subdev_name = chan_cfg->inputs[i].subdev_name; + break; + } + } + + /* if reached maximum. return null */ + if (i == chan_cfg->input_count || (NULL == subdev_name)) + return subdev_info; + + /* loop through the sub device list to get the sub device info */ + for (i = 0; i < vpif_cfg->subdev_count; i++) { + subdev_info = &vpif_cfg->subdev_info[i]; + if (!strcmp(subdev_info->name, subdev_name)) + break; + } + + if (i == vpif_cfg->subdev_count) + return subdev_info; + + /* check if the sub device is registered */ + if (NULL == vpif_obj.sd[i]) + return NULL; + + *sub_device_index = i; + return subdev_info; +} + +/** + * vpif_querystd() - querystd handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + * + * This function is called to detect standard at the selected input + */ +static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + vpif_dbg(2, debug, "vpif_querystd\n"); + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* Call querystd function of decoder device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + querystd, std_id); + if (ret < 0) + vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); + + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_g_std() - get STD handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + */ +static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + vpif_dbg(2, debug, "vpif_g_std\n"); + + *std = ch->video.stdid; + return 0; +} + +/** + * vpif_s_std() - set STD handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + */ +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + vpif_dbg(2, debug, "vpif_s_std\n"); + + if (common->started) { + vpif_err("streaming in progress\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.stdid = *std_id; + + /* Get the information about the standard */ + if (vpif_update_std_info(ch)) { + ret = -EINVAL; + vpif_err("Error getting the standard info\n"); + goto s_std_exit; + } + + /* Configure the default format information */ + vpif_config_format(ch); + + /* set standard in the sub device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core, + s_std, *std_id); + if (ret < 0) + vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); + +s_std_exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_enum_input() - ENUMINPUT handler + * @file: file ptr + * @priv: file handle + * @input: ptr to input structure + */ +static int vpif_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_capture_chan_config *chan_cfg; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + chan_cfg = &config->chan_config[ch->channel_id]; + + if (input->index >= chan_cfg->input_count) { + vpif_dbg(1, debug, "Invalid input index\n"); + return -EINVAL; + } + + memcpy(input, &chan_cfg->inputs[input->index].input, + sizeof(*input)); + return 0; +} + +/** + * vpif_g_input() - Get INPUT handler + * @file: file ptr + * @priv: file handle + * @index: ptr to input index + */ +static int vpif_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + *index = vid_ch->input_idx; + + return 0; +} + +/** + * vpif_s_input() - Set INPUT handler + * @file: file ptr + * @priv: file handle + * @index: input index + */ +static int vpif_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_capture_chan_config *chan_cfg; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_subdev_info *subdev_info; + int ret = 0, sd_index = 0; + u32 input = 0, output = 0; + + chan_cfg = &config->chan_config[ch->channel_id]; + + if (common->started) { + vpif_err("Streaming in progress\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + subdev_info = vpif_map_sub_device_to_input(ch, config, index, + &sd_index); + if (NULL == subdev_info) { + vpif_dbg(1, debug, + "couldn't lookup sub device for the input index\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* first setup input path from sub device to vpif */ + if (config->setup_input_path) { + ret = config->setup_input_path(ch->channel_id, + subdev_info->name); + if (ret < 0) { + vpif_dbg(1, debug, "couldn't setup input path for the" + " sub device %s, for input index %d\n", + subdev_info->name, index); + goto exit; + } + } + + if (subdev_info->can_route) { + input = subdev_info->input; + output = subdev_info->output; + ret = v4l2_subdev_call(vpif_obj.sd[sd_index], video, s_routing, + input, output, 0); + if (ret < 0) { + vpif_dbg(1, debug, "Failed to set input\n"); + goto exit; + } + } + vid_ch->input_idx = index; + ch->curr_subdev_info = subdev_info; + ch->curr_sd_index = sd_index; + /* copy interface parameters to vpif */ + ch->vpifparams.iface = subdev_info->vpif_if; + + /* update tvnorms from the sub device input info */ + ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std; + +exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_enum_fmt_vid_cap() - ENUM_FMT handler + * @file: file ptr + * @priv: file handle + * @index: input index + */ +static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + if (fmt->index != 0) { + vpif_dbg(1, debug, "Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) { + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb"); + fmt->pixelformat = V4L2_PIX_FMT_SBGGR8; + } else { + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); + fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + } + return 0; +} + +/** + * vpif_try_fmt_vid_cap() - TRY_FMT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + return vpif_check_format(ch, pixfmt, 1); +} + + +/** + * vpif_g_fmt_vid_cap() - Set INPUT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + /* Check the validity of the buffer type */ + if (common->fmt.type != fmt->type) + return -EINVAL; + + /* Fill in the information about format */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + *fmt = common->fmt; + mutex_unlock(&common->lock); + return 0; +} + +/** + * vpif_s_fmt_vid_cap() - Set FMT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pixfmt; + int ret = 0; + + vpif_dbg(2, debug, "VIDIOC_S_FMT\n"); + + /* If streaming is started, return error */ + if (common->started) { + vpif_dbg(1, debug, "Streaming is started\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + + pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + ret = vpif_check_format(ch, pixfmt, 0); + + if (ret) + return ret; + /* store the format in the channel object */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + common->fmt = *fmt; + mutex_unlock(&common->lock); + + return 0; +} + +/** + * vpif_querycap() - QUERYCAP handler + * @file: file ptr + * @priv: file handle + * @cap: ptr to v4l2_capability structure + */ +static int vpif_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + + cap->version = VPIF_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "vpif capture", sizeof(cap->driver)); + strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, config->card_name, sizeof(cap->card)); + + return 0; +} + +/** + * vpif_g_priority() - get priority handler + * @file: file ptr + * @priv: file handle + * @prio: ptr to v4l2_priority structure + */ +static int vpif_g_priority(struct file *file, void *priv, + enum v4l2_priority *prio) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *prio = v4l2_prio_max(&ch->prio); + + return 0; +} + +/** + * vpif_s_priority() - set priority handler + * @file: file ptr + * @priv: file handle + * @prio: ptr to v4l2_priority structure + */ +static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_prio_change(&ch->prio, &fh->prio, p); +} + +/** + * vpif_cropcap() - cropcap handler + * @file: file ptr + * @priv: file handle + * @crop: ptr to v4l2_cropcap structure + */ +static int vpif_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type) + return -EINVAL; + + crop->bounds.left = 0; + crop->bounds.top = 0; + crop->bounds.height = common->height; + crop->bounds.width = common->width; + crop->defrect = crop->bounds; + return 0; +} + +/* vpif capture ioctl operations */ +static const struct v4l2_ioctl_ops vpif_ioctl_ops = { + .vidioc_querycap = vpif_querycap, + .vidioc_g_priority = vpif_g_priority, + .vidioc_s_priority = vpif_s_priority, + .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap, + .vidioc_enum_input = vpif_enum_input, + .vidioc_s_input = vpif_s_input, + .vidioc_g_input = vpif_g_input, + .vidioc_reqbufs = vpif_reqbufs, + .vidioc_querybuf = vpif_querybuf, + .vidioc_querystd = vpif_querystd, + .vidioc_s_std = vpif_s_std, + .vidioc_g_std = vpif_g_std, + .vidioc_qbuf = vpif_qbuf, + .vidioc_dqbuf = vpif_dqbuf, + .vidioc_streamon = vpif_streamon, + .vidioc_streamoff = vpif_streamoff, + .vidioc_cropcap = vpif_cropcap, +}; + +/* vpif file operations */ +static struct v4l2_file_operations vpif_fops = { + .owner = THIS_MODULE, + .open = vpif_open, + .release = vpif_release, + .ioctl = video_ioctl2, + .mmap = vpif_mmap, + .poll = vpif_poll +}; + +/* vpif video template */ +static struct video_device vpif_video_template = { + .name = "vpif", + .fops = &vpif_fops, + .minor = -1, + .ioctl_ops = &vpif_ioctl_ops, +}; + +/** + * initialize_vpif() - Initialize vpif data structures + * + * Allocate memory for data structures and initialize them + */ +static int initialize_vpif(void) +{ + int err = 0, i, j; + int free_channel_objects_index; + + /* Default number of buffers should be 3 */ + if ((ch0_numbuffers > 0) && + (ch0_numbuffers < config_params.min_numbuffers)) + ch0_numbuffers = config_params.min_numbuffers; + if ((ch1_numbuffers > 0) && + (ch1_numbuffers < config_params.min_numbuffers)) + ch1_numbuffers = config_params.min_numbuffers; + + /* Set buffer size to min buffers size if it is invalid */ + if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]) + ch0_bufsize = + config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]; + if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]) + ch1_bufsize = + config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]; + + config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers; + config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers; + if (ch0_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO] + = ch0_bufsize; + } + if (ch1_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO] + = ch1_bufsize; + } + + /* Allocate memory for six channel objects */ + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + vpif_obj.dev[i] = + kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!vpif_obj.dev[i]) { + free_channel_objects_index = i; + err = -ENOMEM; + goto vpif_init_free_channel_objects; + } + } + return 0; + +vpif_init_free_channel_objects: + for (j = 0; j < free_channel_objects_index; j++) + kfree(vpif_obj.dev[j]); + return err; +} + +/** + * vpif_probe : This function probes the vpif capture driver + * @pdev: platform device pointer + * + * This creates device entries by register itself to the V4L2 driver and + * initializes fields of each channel objects + */ +static __init int vpif_probe(struct platform_device *pdev) +{ + struct vpif_subdev_info *subdevdata; + struct vpif_capture_config *config; + int i, j, k, m, q, err; + struct i2c_adapter *i2c_adap; + struct channel_obj *ch; + struct common_obj *common; + struct video_device *vfd; + struct resource *res; + int subdev_count; + + vpif_dev = &pdev->dev; + + err = initialize_vpif(); + if (err) { + v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); + return err; + } + + k = 0; + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Capture", + (void *)(&vpif_obj.dev[k]->channel_id))) { + err = -EBUSY; + i--; + goto vpif_int_err; + } + } + k++; + } + + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + for (j = 0; j < i; j++) { + ch = vpif_obj.dev[j]; + video_device_release(ch->video_dev); + } + err = -ENOMEM; + goto vpif_dev_alloc_err; + } + + /* Initialize field of video device */ + *vfd = vpif_video_template; + vfd->v4l2_dev = &vpif_obj.v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), + "DM646x_VPIFCapture_DRIVER_V%d.%d.%d", + (VPIF_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPIF_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPIF_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + ch->video_dev = vfd; + } + + for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + ch->channel_id = j; + common = &(ch->common[VPIF_VIDEO_INDEX]); + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 1 : 0)); + if (err) + goto probe_out; + + video_set_drvdata(ch->video_dev, ch); + + } + + i2c_adap = i2c_get_adapter(1); + config = pdev->dev.platform_data; + + subdev_count = config->subdev_count; + vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, + GFP_KERNEL); + if (vpif_obj.sd == NULL) { + vpif_err("unable to allocate memory for subdevice pointers\n"); + err = -ENOMEM; + goto probe_out; + } + + err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); + if (err) { + v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); + goto probe_subdev_out; + } + + for (i = 0; i < subdev_count; i++) { + subdevdata = &config->subdev_info[i]; + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + subdevdata->name, + &subdevdata->board_info, + NULL); + + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", + subdevdata->name); + + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + v4l2_info(&vpif_obj.v4l2_dev, "DM646x VPIF Capture driver" + " initialized\n"); + + return 0; + +probe_subdev_out: + /* free sub devices memory */ + kfree(vpif_obj.sd); + + j = VPIF_CAPTURE_MAX_DEVICES; +probe_out: + v4l2_device_unregister(&vpif_obj.v4l2_dev); + for (k = 0; k < j; k++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[k]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + +vpif_dev_alloc_err: + k = VPIF_CAPTURE_MAX_DEVICES-1; + res = platform_get_resource(pdev, IORESOURCE_IRQ, k); + i = res->end; + +vpif_int_err: + for (q = k; q >= 0; q--) { + for (m = i; m >= (int)res->start; m--) + free_irq(m, (void *)(&vpif_obj.dev[q]->channel_id)); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, q-1); + if (res) + i = res->end; + } + return err; +} + +/** + * vpif_remove() - driver remove handler + * @device: ptr to platform device structure + * + * The vidoe device is unregistered + */ +static int vpif_remove(struct platform_device *device) +{ + int i; + struct channel_obj *ch; + + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + /* un-register device */ + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + return 0; +} + +/** + * vpif_suspend: vpif device suspend + * + * TODO: Add suspend code here + */ +static int +vpif_suspend(struct device *dev) +{ + return -1; +} + +/** + * vpif_resume: vpif device suspend + * + * TODO: Add resume code here + */ +static int +vpif_resume(struct device *dev) +{ + return -1; +} + +static struct dev_pm_ops vpif_dev_pm_ops = { + .suspend = vpif_suspend, + .resume = vpif_resume, +}; + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif_capture", + .owner = THIS_MODULE, + .pm = &vpif_dev_pm_ops, + }, + .probe = vpif_probe, + .remove = vpif_remove, +}; + +/** + * vpif_init: initialize the vpif driver + * + * This function registers device and driver to the kernel, requests irq + * handler and allocates memory + * for channel objects + */ +static __init int vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} + +/** + * vpif_cleanup : This function clean up the vpif capture resources + * + * This will un-registers device and driver to the kernel, frees + * requested irq handler and de-allocates memory allocated for channel + * objects. + */ +static void vpif_cleanup(void) +{ + struct platform_device *pdev; + struct resource *res; + int irq_num; + int i = 0; + + pdev = container_of(vpif_dev, struct platform_device, dev); + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, + (void *)(&vpif_obj.dev[i]->channel_id)); + i++; + } + + platform_driver_unregister(&vpif_driver); + + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + +/* Function for module initialization and cleanup */ +module_init(vpif_init); +module_exit(vpif_cleanup); diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h new file mode 100644 index 00000000000..4e12ec8cac6 --- /dev/null +++ b/drivers/media/video/davinci/vpif_capture.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VPIF_CAPTURE_H +#define VPIF_CAPTURE_H + +#ifdef __KERNEL__ + +/* Header files */ +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/videobuf-core.h> +#include <media/videobuf-dma-contig.h> +#include <mach/dm646x.h> + +#include "vpif.h" + +/* Macros */ +#define VPIF_MAJOR_RELEASE 0 +#define VPIF_MINOR_RELEASE 0 +#define VPIF_BUILD 1 +#define VPIF_CAPTURE_VERSION_CODE ((VPIF_MAJOR_RELEASE << 16) | \ + (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD) + +#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \ + (V4L2_FIELD_NONE == field)) || \ + (((V4L2_FIELD_INTERLACED == field) || \ + (V4L2_FIELD_SEQ_TB == field)) || \ + (V4L2_FIELD_SEQ_BT == field))) + +#define VPIF_CAPTURE_MAX_DEVICES 2 +#define VPIF_VIDEO_INDEX 0 +#define VPIF_NUMBER_OF_OBJECTS 1 + +/* Enumerated data type to give id to each device per channel */ +enum vpif_channel_id { + VPIF_CHANNEL0_VIDEO = 0, + VPIF_CHANNEL1_VIDEO, +}; + +struct video_obj { + enum v4l2_field buf_field; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* This is to track the last input that is passed to application */ + u32 input_idx; +}; + +struct common_obj { + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Indicates whether streaming started */ + u8 started; + /* Function pointer to set the addresses */ + void (*set_addr) (unsigned long, unsigned long, unsigned long, + unsigned long); + /* offset where Y top starts from the starting of the buffer */ + u32 ytop_off; + /* offset where Y bottom starts from the starting of the buffer */ + u32 ybtm_off; + /* offset where C top starts from the starting of the buffer */ + u32 ctop_off; + /* offset where C bottom starts from the starting of the buffer */ + u32 cbtm_off; + /* Indicates width of the image data */ + u32 width; + /* Indicates height of the image data */ + u32 height; +}; + +struct channel_obj { + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + int usrs; + /* Indicates id of the field which is being displayed */ + u32 field_id; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* Identifies channel */ + enum vpif_channel_id channel_id; + /* index into sd table */ + int curr_sd_index; + /* ptr to current sub device information */ + struct vpif_subdev_info *curr_subdev_info; + /* vpif configuration params */ + struct vpif_params vpifparams; + /* common object array */ + struct common_obj common[VPIF_NUMBER_OF_OBJECTS]; + /* video object */ + struct video_obj video; +}; + +/* File handle structure */ +struct vpif_fh { + /* pointer to channel object for opened device */ + struct channel_obj *channel; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed[VPIF_NUMBER_OF_OBJECTS]; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; + /* Used to indicate channel is initialize or not */ + u8 initialized; +}; + +struct vpif_device { + struct v4l2_device v4l2_dev; + struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; + struct v4l2_subdev **sd; +}; + +struct vpif_config_params { + u8 min_numbuffers; + u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS]; + s8 device_type; + u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; + u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; + u8 default_device[VPIF_CAPTURE_NUM_CHANNELS]; + u8 max_device_type; +}; +/* Struct which keeps track of the line numbers for the sliced vbi service */ +struct vpif_service_line { + u16 service_id; + u16 service_line[2]; +}; +#endif /* End of __KERNEL__ */ +#endif /* VPIF_CAPTURE_H */ diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c new file mode 100644 index 00000000000..c015da813dd --- /dev/null +++ b/drivers/media/video/davinci/vpif_display.c @@ -0,0 +1,1656 @@ +/* + * vpif-display - VPIF display driver + * Display driver for TI DaVinci VPIF + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/string.h> +#include <linux/videodev2.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/version.h> + +#include <asm/irq.h> +#include <asm/page.h> + +#include <media/adv7343.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include <mach/dm646x.h> + +#include "vpif_display.h" +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); +MODULE_LICENSE("GPL"); + +#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) + +#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) +#define vpif_dbg(level, debug, fmt, arg...) \ + v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) + +static int debug = 1; +static u32 ch2_numbuffers = 3; +static u32 ch3_numbuffers = 3; +static u32 ch2_bufsize = 1920 * 1080 * 2; +static u32 ch3_bufsize = 720 * 576 * 2; + +module_param(debug, int, 0644); +module_param(ch2_numbuffers, uint, S_IRUGO); +module_param(ch3_numbuffers, uint, S_IRUGO); +module_param(ch2_bufsize, uint, S_IRUGO); +module_param(ch3_bufsize, uint, S_IRUGO); + +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); +MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); +MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); +MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); + +static struct vpif_config_params config_params = { + .min_numbuffers = 3, + .numbuffers[0] = 3, + .numbuffers[1] = 3, + .min_bufsize[0] = 720 * 480 * 2, + .min_bufsize[1] = 720 * 480 * 2, + .channel_bufsize[0] = 1920 * 1080 * 2, + .channel_bufsize[1] = 720 * 576 * 2, +}; + +static struct vpif_device vpif_obj = { {NULL} }; +static struct device *vpif_dev; + +static const struct vpif_channel_config_params ch_params[] = { + { + "NTSC", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, + 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, + }, + { + "PAL", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, + 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, + }, +}; + +/* + * vpif_uservirt_to_phys: This function is used to convert user + * space virtual address to physical address. + */ +static u32 vpif_uservirt_to_phys(u32 virtp) +{ + struct mm_struct *mm = current->mm; + unsigned long physp = 0; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *)virtp); + } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { + /* this will catch, kernel-allocated, mmaped-to-usermode addr */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, + virtp, nr_pages, 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * buffer_prepare: This is the callback function called from videobuf_qbuf() + * function the buffer is prepared and user space virtual address is converted + * into physical address + */ +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + unsigned long addr; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = common->width; + vb->height = common->height; + vb->size = vb->width * vb->height; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + + /* if user pointer memory mechanism is used, get the physical + * address of the buffer */ + if (V4L2_MEMORY_USERPTR == common->memory) { + if (!vb->baddr) { + vpif_err("buffer_address is 0\n"); + return -EINVAL; + } + + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!ISALIGNED(vb->boff)) + goto buf_align_exit; + } + + addr = vb->boff; + if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { + if (!ISALIGNED(addr + common->ytop_off) || + !ISALIGNED(addr + common->ybtm_off) || + !ISALIGNED(addr + common->ctop_off) || + !ISALIGNED(addr + common->cbtm_off)) + goto buf_align_exit; + } + return 0; + +buf_align_exit: + vpif_err("buffer offset not aligned to 8 bytes\n"); + return -EINVAL; +} + +/* + * vpif_buffer_setup: This function allocates memory for the buffers + */ +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + *size = config_params.channel_bufsize[ch->channel_id]; + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + + return 0; +} + +/* + * vpif_buffer_queue: This function adds the buffer to DMA queue + */ +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &common->dma_queue); + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpif_buffer_release: This function is called from the videobuf layer to + * free memory allocated to the buffers + */ +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + unsigned int buf_size = 0; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != common->memory) + return; + + buf_size = config_params.channel_bufsize[ch->channel_id]; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpif_buffer_setup, + .buf_prepare = vpif_buffer_prepare, + .buf_queue = vpif_buffer_queue, + .buf_release = vpif_buffer_release, +}; +static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; + +static void process_progressive_mode(struct common_obj *common) +{ + unsigned long addr = 0; + + /* Get the next buffer from buffer queue */ + common->next_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&common->next_frm->queue); + /* Mark status of the buffer as active */ + common->next_frm->state = VIDEOBUF_ACTIVE; + + /* Set top and bottom field addrs in VPIF registers */ + addr = videobuf_to_dma_contig(common->next_frm); + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); +} + +static void process_interlaced_mode(int fid, struct common_obj *common) +{ + /* device field id and local field id are in sync */ + /* If this is even field */ + if (0 == fid) { + if (common->cur_frm == common->next_frm) + return; + + /* one frame is displayed If next frame is + * available, release cur_frm and move on */ + /* Copy frame display time */ + do_gettimeofday(&common->cur_frm->ts); + /* Change status of the cur_frm */ + common->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + + } else if (1 == fid) { /* odd field */ + if (list_empty(&common->dma_queue) + || (common->cur_frm != common->next_frm)) { + return; + } + /* one field is displayed configure the next + * frame if it is available else hold on current + * frame */ + /* Get next from the buffer queue */ + process_progressive_mode(common); + + } +} + +/* + * vpif_channel_isr: It changes status of the displayed buffer, takes next + * buffer from the queue and sets its address in VPIF registers + */ +static irqreturn_t vpif_channel_isr(int irq, void *dev_id) +{ + struct vpif_device *dev = &vpif_obj; + struct channel_obj *ch; + struct common_obj *common; + enum v4l2_field field; + int fid = -1, i; + int channel_id = 0; + + channel_id = *(int *)(dev_id); + ch = dev->dev[channel_id]; + field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; + for (i = 0; i < VPIF_NUMOBJECTS; i++) { + common = &ch->common[i]; + /* If streaming is started in this channel */ + if (0 == common->started) + continue; + + if (1 == ch->vpifparams.std_info.frm_fmt) { + if (list_empty(&common->dma_queue)) + continue; + + /* Progressive mode */ + if (!channel_first_int[i][channel_id]) { + /* Mark status of the cur_frm to + * done and unlock semaphore on it */ + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + } + + channel_first_int[i][channel_id] = 0; + process_progressive_mode(common); + } else { + /* Interlaced mode */ + /* If it is first interrupt, ignore it */ + + if (channel_first_int[i][channel_id]) { + channel_first_int[i][channel_id] = 0; + continue; + } + + if (0 == i) { + ch->field_id ^= 1; + /* Get field id from VPIF registers */ + fid = vpif_channel_getfid(ch->channel_id + 2); + /* If fid does not match with stored field id */ + if (fid != ch->field_id) { + /* Make them in sync */ + if (0 == fid) + ch->field_id = fid; + + return IRQ_HANDLED; + } + } + process_interlaced_mode(fid, common); + } + } + + return IRQ_HANDLED; +} + +static int vpif_get_std_info(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + const struct vpif_channel_config_params *config; + + int index; + + std_info->stdid = vid_ch->stdid; + if (!std_info) + return -1; + + for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + config = &ch_params[index]; + if (config->stdid & std_info->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } + + if (index == ARRAY_SIZE(ch_params)) + return -1; + + common->fmt.fmt.pix.width = std_info->width; + common->fmt.fmt.pix.height = std_info->height; + vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", + common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); + + /* Set height and width paramateres */ + ch->common[VPIF_VIDEO_INDEX].height = std_info->height; + ch->common[VPIF_VIDEO_INDEX].width = std_info->width; + + return 0; +} + +/* + * vpif_calculate_offsets: This function calculates buffers offset for Y and C + * in the top and bottom field + */ +static void vpif_calculate_offsets(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_params *vpifparams = &ch->vpifparams; + enum v4l2_field field = common->fmt.fmt.pix.field; + struct video_obj *vid_ch = &ch->video; + unsigned int hpitch, vpitch, sizeimage; + + if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { + if (ch->vpifparams.std_info.frm_fmt) + vid_ch->buf_field = V4L2_FIELD_NONE; + else + vid_ch->buf_field = V4L2_FIELD_INTERLACED; + } else { + vid_ch->buf_field = common->fmt.fmt.pix.field; + } + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + hpitch = common->fmt.fmt.pix.bytesperline; + vpitch = sizeimage / (hpitch * 2); + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + common->ytop_off = 0; + common->ybtm_off = hpitch; + common->ctop_off = sizeimage / 2; + common->cbtm_off = sizeimage / 2 + hpitch; + } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { + common->ytop_off = 0; + common->ybtm_off = sizeimage / 4; + common->ctop_off = sizeimage / 2; + common->cbtm_off = common->ctop_off + sizeimage / 4; + } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { + common->ybtm_off = 0; + common->ytop_off = sizeimage / 4; + common->cbtm_off = sizeimage / 2; + common->ctop_off = common->cbtm_off + sizeimage / 4; + } + + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + vpifparams->video_params.storage_mode = 1; + } else { + vpifparams->video_params.storage_mode = 0; + } + + if (ch->vpifparams.std_info.frm_fmt == 1) { + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } else { + if ((field == V4L2_FIELD_ANY) || + (field == V4L2_FIELD_INTERLACED)) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline * 2; + else + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } + + ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; +} + +static void vpif_config_format(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + common->fmt.fmt.pix.field = V4L2_FIELD_ANY; + if (config_params.numbuffers[ch->channel_id] == 0) + common->memory = V4L2_MEMORY_USERPTR; + else + common->memory = V4L2_MEMORY_MMAP; + + common->fmt.fmt.pix.sizeimage = + config_params.channel_bufsize[ch->channel_id]; + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +} + +static int vpif_check_format(struct channel_obj *ch, + struct v4l2_pix_format *pixfmt) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + enum v4l2_field field = pixfmt->field; + u32 sizeimage, hpitch, vpitch; + + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) + goto invalid_fmt_exit; + + if (!(VPIF_VALID_FIELD(field))) + goto invalid_fmt_exit; + + if (pixfmt->bytesperline <= 0) + goto invalid_pitch_exit; + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + hpitch = pixfmt->bytesperline; + vpitch = sizeimage / (hpitch * 2); + + /* Check for valid value of pitch */ + if ((hpitch < ch->vpifparams.std_info.width) || + (vpitch < ch->vpifparams.std_info.height)) + goto invalid_pitch_exit; + + /* Check for 8 byte alignment */ + if (!ISALIGNED(hpitch)) { + vpif_err("invalid pitch alignment\n"); + return -EINVAL; + } + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + + return 0; + +invalid_fmt_exit: + vpif_err("invalid field format\n"); + return -EINVAL; + +invalid_pitch_exit: + vpif_err("invalid pitch\n"); + return -EINVAL; +} + +static void vpif_config_addr(struct channel_obj *ch, int muxmode) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { + common->set_addr = ch3_set_videobuf_addr; + } else { + if (2 == muxmode) + common->set_addr = ch2_set_videobuf_addr_yc_nmux; + else + common->set_addr = ch2_set_videobuf_addr; + } +} + +/* + * vpif_mmap: It is used to map kernel space buffers into user spaces + */ +static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vpif_fh *fh = filep->private_data; + struct common_obj *common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + return videobuf_mmap_mapper(&common->buffer_queue, vma); +} + +/* + * vpif_poll: It is used for select/poll system call + */ +static unsigned int vpif_poll(struct file *filep, poll_table *wait) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->started) + return videobuf_poll_stream(filep, &common->buffer_queue, wait); + + return 0; +} + +/* + * vpif_open: It creates object of file handle structure and stores it in + * private_data member of filepointer + */ +static int vpif_open(struct file *filep) +{ + struct video_device *vdev = video_devdata(filep); + struct channel_obj *ch = NULL; + struct vpif_fh *fh = NULL; + + ch = video_get_drvdata(vdev); + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); + if (fh == NULL) { + vpif_err("unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + + /* store pointer to fh in private_data member of filep */ + filep->private_data = fh; + fh->channel = ch; + fh->initialized = 0; + if (!ch->initialized) { + fh->initialized = 1; + ch->initialized = 1; + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + } + + /* Increment channel usrs counter */ + atomic_inc(&ch->usrs); + /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ + fh->io_allowed[VPIF_VIDEO_INDEX] = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&ch->prio, &fh->prio); + + return 0; +} + +/* + * vpif_release: This function deletes buffer queue, frees the buffers and + * the vpif file handle + */ +static int vpif_release(struct file *filep) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* if this instance is doing IO */ + if (fh->io_allowed[VPIF_VIDEO_INDEX]) { + /* Reset io_usrs member of channel object */ + common->io_usrs = 0; + /* Disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + common->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); + common->numbuffers = + config_params.numbuffers[ch->channel_id]; + } + + mutex_unlock(&common->lock); + + /* Decrement channel usrs counter */ + atomic_dec(&ch->usrs); + /* If this file handle has initialize encoder device, reset it */ + if (fh->initialized) + ch->initialized = 0; + + /* Close the priority */ + v4l2_prio_close(&ch->prio, &fh->prio); + filep->private_data = NULL; + fh->initialized = 0; + kfree(fh); + + return 0; +} + +/* functions implementing ioctls */ + +static int vpif_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpif_display_config *config = vpif_dev->platform_data; + + cap->version = VPIF_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "vpif display", sizeof(cap->driver)); + strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, config->card_name, sizeof(cap->card)); + + return 0; +} + +static int vpif_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index != 0) { + vpif_err("Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); + fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + + return 0; +} + +static int vpif_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + /* Check the validity of the buffer type */ + if (common->fmt.type != fmt->type) + return -EINVAL; + + /* Fill in the information about format */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + *fmt = common->fmt; + mutex_unlock(&common->lock); + return 0; +} + +static int vpif_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct v4l2_pix_format *pixfmt; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + + /* Check for the priority */ + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + fh->initialized = 1; + } + + if (common->started) { + vpif_dbg(1, debug, "Streaming in progress\n"); + return -EBUSY; + } + + pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + ret = vpif_check_format(ch, pixfmt); + if (ret) + return ret; + + /* store the pix format in the channel object */ + common->fmt.fmt.pix = *pixfmt; + /* store the format in the channel object */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + common->fmt = *fmt; + mutex_unlock(&common->lock); + + return 0; +} + +static int vpif_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + int ret = 0; + + ret = vpif_check_format(ch, pixfmt); + if (ret) { + *pixfmt = common->fmt.fmt.pix; + pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; + } + + return ret; +} + +static int vpif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + enum v4l2_field field; + u8 index = 0; + int ret = 0; + + /* This file handle has not initialized the channel, + It is not allowed to do settings */ + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_err("Channel Busy\n"); + return -EBUSY; + } + } + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) + return -EINVAL; + + index = VPIF_VIDEO_INDEX; + + common = &ch->common[index]; + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->fmt.type != reqbuf->type) { + ret = -EINVAL; + goto reqbuf_exit; + } + + if (0 != common->io_usrs) { + ret = -EBUSY; + goto reqbuf_exit; + } + + if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) + field = V4L2_FIELD_INTERLACED; + else + field = common->fmt.fmt.pix.field; + } else { + field = V4L2_VBI_INTERLACED; + } + + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&common->buffer_queue, + &video_qops, NULL, + &common->irqlock, + reqbuf->type, field, + sizeof(struct videobuf_buffer), fh); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed[index] = 1; + /* Increment io usrs member of channel object to 1 */ + common->io_usrs = 1; + /* Store type of memory requested in channel object */ + common->memory = reqbuf->memory; + INIT_LIST_HEAD(&common->dma_queue); + + /* Allocate buffers */ + ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); + +reqbuf_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *tbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->fmt.type != tbuf->type) + return -EINVAL; + + return videobuf_querybuf(&common->buffer_queue, tbuf); +} + +static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_buffer tbuf = *buf; + struct videobuf_buffer *buf1; + unsigned long addr = 0; + unsigned long flags; + int ret = 0; + + if (common->fmt.type != tbuf.type) + return -EINVAL; + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!(list_empty(&common->dma_queue)) || + (common->cur_frm != common->next_frm) || + !(common->started) || + (common->started && (0 == ch->field_id))) + return videobuf_qbuf(&common->buffer_queue, buf); + + /* bufferqueue is empty store buffer address in VPIF registers */ + mutex_lock(&common->buffer_queue.vb_lock); + buf1 = common->buffer_queue.bufs[tbuf.index]; + if (buf1->memory != tbuf.memory) { + vpif_err("invalid buffer type\n"); + goto qbuf_exit; + } + + if ((buf1->state == VIDEOBUF_QUEUED) || + (buf1->state == VIDEOBUF_ACTIVE)) { + vpif_err("invalid state\n"); + goto qbuf_exit; + } + + switch (buf1->memory) { + case V4L2_MEMORY_MMAP: + if (buf1->baddr == 0) + goto qbuf_exit; + break; + + case V4L2_MEMORY_USERPTR: + if (tbuf.length < buf1->bsize) + goto qbuf_exit; + + if ((VIDEOBUF_NEEDS_INIT != buf1->state) + && (buf1->baddr != tbuf.m.userptr)) + vpif_buffer_release(&common->buffer_queue, buf1); + buf1->baddr = tbuf.m.userptr; + break; + + default: + goto qbuf_exit; + } + + local_irq_save(flags); + ret = vpif_buffer_prepare(&common->buffer_queue, buf1, + common->buffer_queue.field); + if (ret < 0) { + local_irq_restore(flags); + goto qbuf_exit; + } + + buf1->state = VIDEOBUF_ACTIVE; + addr = buf1->boff; + common->next_frm = buf1; + if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + } + + local_irq_restore(flags); + list_add_tail(&buf1->stream, &common->buffer_queue.stream); + mutex_unlock(&common->buffer_queue.vb_lock); + return 0; + +qbuf_exit: + mutex_unlock(&common->buffer_queue.vb_lock); + return -EINVAL; +} + +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (!(*std_id & DM646X_V4L2_STD)) + return -EINVAL; + + if (common->started) { + vpif_err("streaming in progress\n"); + return -EBUSY; + } + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.stdid = *std_id; + /* Get the information about the standard */ + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + if ((ch->vpifparams.std_info.width * + ch->vpifparams.std_info.height * 2) > + config_params.channel_bufsize[ch->channel_id]) { + vpif_err("invalid std for this size\n"); + ret = -EINVAL; + goto s_std_exit; + } + + common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; + /* Configure the default format information */ + vpif_config_format(ch); + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_std_output, *std_id); + if (ret < 0) { + vpif_err("Failed to set output standard\n"); + goto s_std_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, + s_std, *std_id); + if (ret < 0) + vpif_err("Failed to set standard for sub devices\n"); + +s_std_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *std = ch->video.stdid; + return 0; +} + +static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + return videobuf_dqbuf(&common->buffer_queue, p, + (file->f_flags & O_NONBLOCK)); +} + +static int vpif_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; + struct vpif_params *vpif = &ch->vpifparams; + struct vpif_display_config *vpif_config_data = + vpif_dev->platform_data; + unsigned long addr = 0; + int ret = 0; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + /* If Streaming is already started, return error */ + if (common->started) { + vpif_err("channel->started\n"); + return -EBUSY; + } + + if ((ch->channel_id == VPIF_CHANNEL2_VIDEO + && oth_ch->common[VPIF_VIDEO_INDEX].started && + ch->vpifparams.std_info.ycmux_mode == 0) + || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) + && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { + vpif_err("other channel is using\n"); + return -EBUSY; + } + + ret = vpif_check_format(ch, &common->fmt.fmt.pix); + if (ret < 0) + return ret; + + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); + if (ret < 0) { + vpif_err("videobuf_streamon\n"); + return ret; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* If buffer queue is empty, return error */ + if (list_empty(&common->dma_queue)) { + vpif_err("buffer queue is empty\n"); + ret = -EIO; + goto streamon_exit; + } + + /* Get the next frame from the buffer queue */ + common->next_frm = common->cur_frm = + list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + + list_del(&common->cur_frm->queue); + /* Mark state of the current frame to active */ + common->cur_frm->state = VIDEOBUF_ACTIVE; + + /* Initialize field_id and started member */ + ch->field_id = 0; + common->started = 1; + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + addr = common->cur_frm->boff; + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + + if ((ch->vpifparams.std_info.frm_fmt && + ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) + && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) + || (!ch->vpifparams.std_info.frm_fmt + && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { + vpif_err("conflict in field format and std format\n"); + ret = -EINVAL; + goto streamon_exit; + } + + /* clock settings */ + ret = + vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode, + ch->vpifparams.std_info.hd_sd); + if (ret < 0) { + vpif_err("can't set clock\n"); + goto streamon_exit; + } + + /* set the parameters and addresses */ + ret = vpif_set_video_params(vpif, ch->channel_id + 2); + if (ret < 0) + goto streamon_exit; + + common->started = ret; + vpif_config_addr(ch, ret); + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + + /* Set interrupt for both the fields in VPIF + Register enable channel in VPIF register */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + channel2_intr_assert(); + channel2_intr_enable(1); + enable_channel2(1); + } + + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) + || (common->started == 2)) { + channel3_intr_assert(); + channel3_intr_enable(1); + enable_channel3(1); + } + channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; + } + +streamon_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!common->started) { + vpif_err("channel->started\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + } + + common->started = 0; + mutex_unlock(&common->lock); + + return videobuf_streamoff(&common->buffer_queue); +} + +static int vpif_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) + return -EINVAL; + + crop->bounds.left = crop->bounds.top = 0; + crop->defrect.left = crop->defrect.top = 0; + crop->defrect.height = crop->bounds.height = common->height; + crop->defrect.width = crop->bounds.width = common->width; + + return 0; +} + +static int vpif_enum_output(struct file *file, void *fh, + struct v4l2_output *output) +{ + + struct vpif_display_config *config = vpif_dev->platform_data; + + if (output->index >= config->output_count) { + vpif_dbg(1, debug, "Invalid output index\n"); + return -EINVAL; + } + + strcpy(output->name, config->output[output->index]); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->std = DM646X_V4L2_STD; + + return 0; +} + +static int vpif_s_output(struct file *file, void *priv, unsigned int i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->started) { + vpif_err("Streaming in progress\n"); + ret = -EBUSY; + goto s_output_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_routing, 0, i, 0); + + if (ret < 0) + vpif_err("Failed to set output standard\n"); + + vid_ch->output_id = i; + +s_output_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_output(struct file *file, void *priv, unsigned int *i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + *i = vid_ch->output_id; + + return 0; +} + +static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *p = v4l2_prio_max(&ch->prio); + + return 0; +} + +static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_prio_change(&ch->prio, &fh->prio, p); +} + +/* vpif display ioctl operations */ +static const struct v4l2_ioctl_ops vpif_ioctl_ops = { + .vidioc_querycap = vpif_querycap, + .vidioc_g_priority = vpif_g_priority, + .vidioc_s_priority = vpif_s_priority, + .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, + .vidioc_reqbufs = vpif_reqbufs, + .vidioc_querybuf = vpif_querybuf, + .vidioc_qbuf = vpif_qbuf, + .vidioc_dqbuf = vpif_dqbuf, + .vidioc_streamon = vpif_streamon, + .vidioc_streamoff = vpif_streamoff, + .vidioc_s_std = vpif_s_std, + .vidioc_g_std = vpif_g_std, + .vidioc_enum_output = vpif_enum_output, + .vidioc_s_output = vpif_s_output, + .vidioc_g_output = vpif_g_output, + .vidioc_cropcap = vpif_cropcap, +}; + +static const struct v4l2_file_operations vpif_fops = { + .owner = THIS_MODULE, + .open = vpif_open, + .release = vpif_release, + .ioctl = video_ioctl2, + .mmap = vpif_mmap, + .poll = vpif_poll +}; + +static struct video_device vpif_video_template = { + .name = "vpif", + .fops = &vpif_fops, + .minor = -1, + .ioctl_ops = &vpif_ioctl_ops, + .tvnorms = DM646X_V4L2_STD, + .current_norm = V4L2_STD_625_50, + +}; + +/*Configure the channels, buffer sizei, request irq */ +static int initialize_vpif(void) +{ + int free_channel_objects_index; + int free_buffer_channel_index; + int free_buffer_index; + int err = 0, i, j; + + /* Default number of buffers should be 3 */ + if ((ch2_numbuffers > 0) && + (ch2_numbuffers < config_params.min_numbuffers)) + ch2_numbuffers = config_params.min_numbuffers; + if ((ch3_numbuffers > 0) && + (ch3_numbuffers < config_params.min_numbuffers)) + ch3_numbuffers = config_params.min_numbuffers; + + /* Set buffer size to min buffers size if invalid buffer size is + * given */ + if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) + ch2_bufsize = + config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; + if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) + ch3_bufsize = + config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; + + config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; + + if (ch2_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = + ch2_bufsize; + } + config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; + + if (ch3_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = + ch3_bufsize; + } + + /* Allocate memory for six channel objects */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + vpif_obj.dev[i] = + kmalloc(sizeof(struct channel_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!vpif_obj.dev[i]) { + free_channel_objects_index = i; + err = -ENOMEM; + goto vpif_init_free_channel_objects; + } + } + + free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; + free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; + free_buffer_index = config_params.numbuffers[i - 1]; + + return 0; + +vpif_init_free_channel_objects: + for (j = 0; j < free_channel_objects_index; j++) + kfree(vpif_obj.dev[j]); + return err; +} + +/* + * vpif_probe: This function creates device entries by register itself to the + * V4L2 driver and initializes fields of each channel objects + */ +static __init int vpif_probe(struct platform_device *pdev) +{ + struct vpif_subdev_info *subdevdata; + struct vpif_display_config *config; + int i, j = 0, k, q, m, err = 0; + struct i2c_adapter *i2c_adap; + struct vpif_config *config; + struct common_obj *common; + struct channel_obj *ch; + struct video_device *vfd; + struct resource *res; + int subdev_count; + + vpif_dev = &pdev->dev; + + err = initialize_vpif(); + + if (err) { + v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); + return err; + } + + err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); + if (err) { + v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); + return err; + } + + k = 0; + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Display", + (void *)(&vpif_obj.dev[k]->channel_id))) { + err = -EBUSY; + goto vpif_int_err; + } + } + k++; + } + + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (vfd == NULL) { + for (j = 0; j < i; j++) { + ch = vpif_obj.dev[j]; + video_device_release(ch->video_dev); + } + err = -ENOMEM; + goto vpif_int_err; + } + + /* Initialize field of video device */ + *vfd = vpif_video_template; + vfd->v4l2_dev = &vpif_obj.v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), + "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d", + (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPIF_DISPLAY_VERSION_CODE) & 0xff); + + /* Set video_dev to the video device */ + ch->video_dev = vfd; + } + + for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + /* Initialize field of the channel objects */ + atomic_set(&ch->usrs, 0); + for (k = 0; k < VPIF_NUMOBJECTS; k++) { + ch->common[k].numbuffers = 0; + common = &ch->common[k]; + common->io_usrs = 0; + common->started = 0; + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + common->numbuffers = 0; + common->set_addr = NULL; + common->ytop_off = common->ybtm_off = 0; + common->ctop_off = common->cbtm_off = 0; + common->cur_frm = common->next_frm = NULL; + memset(&common->fmt, 0, sizeof(common->fmt)); + common->numbuffers = config_params.numbuffers[k]; + + } + ch->initialized = 0; + ch->channel_id = j; + if (j < 2) + ch->common[VPIF_VIDEO_INDEX].numbuffers = + config_params.numbuffers[ch->channel_id]; + else + ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; + + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + ch->common[VPIF_VIDEO_INDEX].fmt.type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + + /* register video device */ + vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", + (int)ch, (int)&ch->video_dev); + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 3 : 2)); + if (err < 0) + goto probe_out; + + video_set_drvdata(ch->video_dev, ch); + } + + i2c_adap = i2c_get_adapter(1); + config = pdev->dev.platform_data; + subdev_count = config->subdev_count; + subdevdata = config->subdevinfo; + vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, + GFP_KERNEL); + if (vpif_obj.sd == NULL) { + vpif_err("unable to allocate memory for subdevice pointers\n"); + err = -ENOMEM; + goto probe_out; + } + + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, subdevdata[i].name, + &subdevdata[i].board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + + return 0; + +probe_subdev_out: + kfree(vpif_obj.sd); +probe_out: + for (k = 0; k < j; k++) { + ch = vpif_obj.dev[k]; + video_unregister_device(ch->video_dev); + video_device_release(ch->video_dev); + ch->video_dev = NULL; + } +vpif_int_err: + v4l2_device_unregister(&vpif_obj.v4l2_dev); + vpif_err("VPIF IRQ request failed\n"); + for (q = k; k >= 0; k--) { + for (m = i; m >= res->start; m--) + free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id)); + res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1); + m = res->end; + } + + return err; +} + +/* + * vpif_remove: It un-register channels from V4L2 driver + */ +static int vpif_remove(struct platform_device *device) +{ + struct channel_obj *ch; + int i; + + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + /* un-register device */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + + ch->video_dev = NULL; + } + + return 0; +} + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif_display", + .owner = THIS_MODULE, + }, + .probe = vpif_probe, + .remove = vpif_remove, +}; + +static __init int vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} + +/* + * vpif_cleanup: This function un-registers device and driver to the kernel, + * frees requested irq handler and de-allocates memory allocated for channel + * objects. + */ +static void vpif_cleanup(void) +{ + struct platform_device *pdev; + struct resource *res; + int irq_num; + int i = 0; + + pdev = container_of(vpif_dev, struct platform_device, dev); + + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, + (void *)(&vpif_obj.dev[i]->channel_id)); + i++; + } + + platform_driver_unregister(&vpif_driver); + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + +module_init(vpif_init); +module_exit(vpif_cleanup); diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h new file mode 100644 index 00000000000..a2a7cd166bb --- /dev/null +++ b/drivers/media/video/davinci/vpif_display.h @@ -0,0 +1,175 @@ +/* + * DM646x display header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DAVINCIHD_DISPLAY_H +#define DAVINCIHD_DISPLAY_H + +/* Header files */ +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/videobuf-core.h> +#include <media/videobuf-dma-contig.h> + +#include "vpif.h" + +/* Macros */ +#define VPIF_MAJOR_RELEASE (0) +#define VPIF_MINOR_RELEASE (0) +#define VPIF_BUILD (1) + +#define VPIF_DISPLAY_VERSION_CODE \ + ((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD) + +#define VPIF_VALID_FIELD(field) \ + (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \ + (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \ + (V4L2_FIELD_SEQ_BT == field))) + +#define VPIF_DISPLAY_MAX_DEVICES (2) +#define VPIF_SLICED_BUF_SIZE (256) +#define VPIF_SLICED_MAX_SERVICES (3) +#define VPIF_VIDEO_INDEX (0) +#define VPIF_VBI_INDEX (1) +#define VPIF_HBI_INDEX (2) + +/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/ +#define VPIF_NUMOBJECTS (1) + +/* Macros */ +#define ISALIGNED(a) (0 == ((a) & 7)) + +/* enumerated data types */ +/* Enumerated data type to give id to each device per channel */ +enum vpif_channel_id { + VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */ + VPIF_CHANNEL3_VIDEO, /* Channel3 Video */ +}; + +/* structures */ + +struct video_obj { + enum v4l2_field buf_field; + u32 latest_only; /* indicate whether to return + * most recent displayed frame only */ + v4l2_std_id stdid; /* Currently selected or default + * standard */ + u32 output_id; /* Current output id */ +}; + +struct vbi_obj { + int num_services; + struct vpif_vbi_params vbiparams; /* vpif parameters for the raw + * vbi data */ +}; + +struct common_obj { + /* Buffer specific parameters */ + u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for + * storing frames */ + u32 numbuffers; /* number of buffers */ + struct videobuf_buffer *cur_frm; /* Pointer pointing to current + * videobuf_buffer */ + struct videobuf_buffer *next_frm; /* Pointer pointing to next + * videobuf_buffer */ + enum v4l2_memory memory; /* This field keeps track of + * type of buffer exchange + * method user has selected */ + struct v4l2_format fmt; /* Used to store the format */ + struct videobuf_queue buffer_queue; /* Buffer queue used in + * video-buf */ + struct list_head dma_queue; /* Queue of filled frames */ + spinlock_t irqlock; /* Used in video-buf */ + + /* channel specific parameters */ + struct mutex lock; /* lock used to access this + * structure */ + u32 io_usrs; /* number of users performing + * IO */ + u8 started; /* Indicates whether streaming + * started */ + u32 ytop_off; /* offset of Y top from the + * starting of the buffer */ + u32 ybtm_off; /* offset of Y bottom from the + * starting of the buffer */ + u32 ctop_off; /* offset of C top from the + * starting of the buffer */ + u32 cbtm_off; /* offset of C bottom from the + * starting of the buffer */ + /* Function pointer to set the addresses */ + void (*set_addr) (unsigned long, unsigned long, + unsigned long, unsigned long); + u32 height; + u32 width; +}; + +struct channel_obj { + /* V4l2 specific parameters */ + struct video_device *video_dev; /* Identifies video device for + * this channel */ + struct v4l2_prio_state prio; /* Used to keep track of state of + * the priority */ + atomic_t usrs; /* number of open instances of + * the channel */ + u32 field_id; /* Indicates id of the field + * which is being displayed */ + u8 initialized; /* flag to indicate whether + * encoder is initialized */ + + enum vpif_channel_id channel_id;/* Identifies channel */ + struct vpif_params vpifparams; + struct common_obj common[VPIF_NUMOBJECTS]; + struct video_obj video; + struct vbi_obj vbi; +}; + +/* File handle structure */ +struct vpif_fh { + struct channel_obj *channel; /* pointer to channel object for + * opened device */ + u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle + * is doing IO */ + enum v4l2_priority prio; /* Used to keep track priority of + * this instance */ + u8 initialized; /* Used to keep track of whether this + * file handle has initialized + * channel or not */ +}; + +/* vpif device structure */ +struct vpif_device { + struct v4l2_device v4l2_dev; + struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; + struct v4l2_subdev **sd; + +}; + +struct vpif_config_params { + u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; + u8 min_numbuffers; +}; + +/* Struct which keeps track of the line numbers for the sliced vbi service */ +struct vpif_service_line { + u16 service_id; + u16 service_line[2]; + u16 enc_service_id; + u8 bytestowrite; +}; + +#endif /* DAVINCIHD_DISPLAY_H */ diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c new file mode 100644 index 00000000000..6d709ca8cfb --- /dev/null +++ b/drivers/media/video/davinci/vpss.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * 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. + * + * common vpss driver for all video drivers. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/compiler.h> +#include <linux/io.h> +#include <mach/hardware.h> +#include <media/davinci/vpss.h> + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPSS Driver"); +MODULE_AUTHOR("Texas Instruments"); + +/* DM644x defines */ +#define DM644X_SBL_PCR_VPSS (4) + +/* vpss BL register offsets */ +#define DM355_VPSSBL_CCDCMUX 0x1c +/* vpss CLK register offsets */ +#define DM355_VPSSCLK_CLKCTRL 0x04 +/* masks and shifts */ +#define VPSS_HSSISEL_SHIFT 4 + +/* + * vpss operations. Depends on platform. Not all functions are available + * on all platforms. The api, first check if a functio is available before + * invoking it. In the probe, the function ptrs are intialized based on + * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. + */ +struct vpss_hw_ops { + /* enable clock */ + int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); + /* select input to ccdc */ + void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); + /* clear wbl overlflow bit */ + int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); +}; + +/* vpss configuration */ +struct vpss_oper_config { + __iomem void *vpss_bl_regs_base; + __iomem void *vpss_regs_base; + struct resource *r1; + resource_size_t len1; + struct resource *r2; + resource_size_t len2; + char vpss_name[32]; + spinlock_t vpss_lock; + struct vpss_hw_ops hw_ops; +}; + +static struct vpss_oper_config oper_cfg; + +/* register access routines */ +static inline u32 bl_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_bl_regs_base + offset); +} + +static inline void bl_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset); +} + +static inline u32 vpss_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_regs_base + offset); +} + +static inline void vpss_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_regs_base + offset); +} + +static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); +} + +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + if (!oper_cfg.hw_ops.select_ccdc_source) + return -1; + + dm355_select_ccdc_source(src_sel); + return 0; +} +EXPORT_SYMBOL(vpss_select_ccdc_source); + +static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + u32 mask = 1, val; + + if (wbl_sel < VPSS_PCR_AEW_WBL_0 || + wbl_sel > VPSS_PCR_CCDC_WBL_O) + return -1; + + /* writing a 0 clear the overflow */ + mask = ~(mask << wbl_sel); + val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; + bl_regw(val, DM644X_SBL_PCR_VPSS); + return 0; +} + +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + if (!oper_cfg.hw_ops.clear_wbl_overflow) + return -1; + + return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); +} +EXPORT_SYMBOL(vpss_clear_wbl_overflow); + +/* + * dm355_enable_clock - Enable VPSS Clock + * @clock_sel: CLock to be enabled/disabled + * @en: enable/disable flag + * + * This is called to enable or disable a vpss clock + */ +static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + unsigned long flags; + u32 utemp, mask = 0x1, shift = 0; + + switch (clock_sel) { + case VPSS_VPBE_CLOCK: + /* nothing since lsb */ + break; + case VPSS_VENC_CLOCK_SEL: + shift = 2; + break; + case VPSS_CFALD_CLOCK: + shift = 3; + break; + case VPSS_H3A_CLOCK: + shift = 4; + break; + case VPSS_IPIPE_CLOCK: + shift = 5; + break; + case VPSS_CCDC_CLOCK: + shift = 6; + break; + default: + printk(KERN_ERR "dm355_enable_clock:" + " Invalid selector: %d\n", clock_sel); + return -1; + } + + spin_lock_irqsave(&oper_cfg.vpss_lock, flags); + utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); + if (!en) + utemp &= ~(mask << shift); + else + utemp |= (mask << shift); + + vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); + spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); + return 0; +} + +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + if (!oper_cfg.hw_ops.enable_clock) + return -1; + + return oper_cfg.hw_ops.enable_clock(clock_sel, en); +} +EXPORT_SYMBOL(vpss_enable_clock); + +static int __init vpss_probe(struct platform_device *pdev) +{ + int status, dm355 = 0; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENOENT; + } + strcpy(oper_cfg.vpss_name, pdev->dev.platform_data); + + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) + dm355 = 1; + else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) { + dev_err(&pdev->dev, "vpss driver not supported on" + " this platform\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name); + oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!oper_cfg.r1) + return -ENOENT; + + oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1; + + oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1, + oper_cfg.r1->name); + if (!oper_cfg.r1) + return -EBUSY; + + oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1); + if (!oper_cfg.vpss_bl_regs_base) { + status = -EBUSY; + goto fail1; + } + + if (dm355) { + oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!oper_cfg.r2) { + status = -ENOENT; + goto fail2; + } + oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1; + oper_cfg.r2 = request_mem_region(oper_cfg.r2->start, + oper_cfg.len2, + oper_cfg.r2->name); + if (!oper_cfg.r2) { + status = -EBUSY; + goto fail2; + } + + oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start, + oper_cfg.len2); + if (!oper_cfg.vpss_regs_base) { + status = -EBUSY; + goto fail3; + } + } + + if (dm355) { + oper_cfg.hw_ops.enable_clock = dm355_enable_clock; + oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; + } else + oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + + spin_lock_init(&oper_cfg.vpss_lock); + dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name); + return 0; + +fail3: + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); +fail2: + iounmap(oper_cfg.vpss_bl_regs_base); +fail1: + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + return status; +} + +static int vpss_remove(struct platform_device *pdev) +{ + iounmap(oper_cfg.vpss_bl_regs_base); + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) { + iounmap(oper_cfg.vpss_regs_base); + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + } + return 0; +} + +static struct platform_driver vpss_driver = { + .driver = { + .name = "vpss", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpss_remove), + .probe = vpss_probe, +}; + +static void vpss_exit(void) +{ + platform_driver_unregister(&vpss_driver); +} + +static int __init vpss_init(void) +{ + return platform_driver_register(&vpss_driver); +} +subsys_initcall(vpss_init); +module_exit(vpss_exit); diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 6524b493e03..c7be0e09782 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -36,6 +36,7 @@ config VIDEO_EM28XX_DVB depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 8137a8c94bf..d0f093d1d0d 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,5 +1,5 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ - em28xx-input.o + em28xx-input.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 320f1f60276..bdb249bd9d5 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -170,6 +170,19 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { { -1, -1, -1, -1}, }; +/* eb1a:2868 Reddo DVB-C USB TV Box + GPIO4 - CU1216L NIM + Other GPIOs seems to be don't care. */ +static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xde, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7f, 0xff, 10}, + {EM28XX_R08_GPIO, 0x6f, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {-1, -1, -1, -1}, +}; /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { @@ -218,7 +231,7 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "EM2710/EM2750/EM2751 webcam grabber", - .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, .tuner_type = TUNER_ABSENT, .is_webcam = 1, .input = { { @@ -299,6 +312,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -318,6 +332,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -342,6 +357,7 @@ struct em28xx_board em28xx_boards[] = { TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, + .has_ir_i2c = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -558,6 +574,27 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2861_BOARD_GADMEI_UTV330PLUS] = { + .name = "Gadmei UTV330+", + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .ir_codes = &ir_codes_gadmei_rm008z_table, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2860_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -622,22 +659,27 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_TNF_5335MF, + .xclk = EM28XX_XCLK_I2S_MSB_TIMING | + EM28XX_XCLK_FREQUENCY_12MHZ, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, + .has_msp34xx = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, } }, }, @@ -710,7 +752,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -735,7 +777,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -761,7 +803,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -787,7 +829,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -813,7 +855,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_pinnacle_pctv_hd, + .ir_codes = &ir_codes_pinnacle_pctv_hd_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -839,7 +881,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_ati_tv_wonder_hd_600, + .ir_codes = &ir_codes_ati_tv_wonder_hd_600_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -865,6 +907,8 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, + .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -932,6 +976,7 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, + .has_ir_i2c = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, @@ -1005,7 +1050,8 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " + "/ Kworld DVD Maker 2", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -1415,7 +1461,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .decoder = EM28XX_TVP5150, .tuner_gpio = default_tuner_gpio, - .ir_codes = ir_codes_kaiomy, + .ir_codes = &ir_codes_kaiomy_table, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1515,7 +1561,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = evga_indtube_digital, - .ir_codes = ir_codes_evga_indtube, + .ir_codes = &ir_codes_evga_indtube_table, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1533,6 +1579,14 @@ struct em28xx_board em28xx_boards[] = { .gpio = evga_indtube_analog, } }, }, + /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + + Infineon TUA6034) */ + [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { + .name = "Reddo DVB-C USB TV Box", + .tuner_type = TUNER_ABSENT, + .has_dvb = 1, + .dvb_gpio = reddo_dvb_c_usb_box, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1544,6 +1598,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2750_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2710), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), @@ -1558,6 +1614,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2883), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2868), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, { USB_DEVICE(0xeb1a, 0xe303), @@ -1584,6 +1642,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0x1b80, 0xe302), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ + { USB_DEVICE(0x1b80, 0xe304), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -1642,6 +1702,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, { USB_DEVICE(0x04bb, 0x0515), .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, + { USB_DEVICE(0xeb1a, 0x50a6), + .driver_info = EM2860_BOARD_GADMEI_UTV330 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1654,9 +1716,10 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, - {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, + {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1665,6 +1728,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, + {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ @@ -1723,6 +1787,25 @@ static inline void em28xx_set_model(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ; } + +/* FIXME: Should be replaced by a proper mt9m111 driver */ +static int em28xx_initialize_mt9m111(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ + { 0x0d, 0x00, 0x00, }, + { 0x0a, 0x00, 0x21, }, + { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + + /* FIXME: Should be replaced by a proper mt9m001 driver */ static int em28xx_initialize_mt9m001(struct em28xx *dev) { @@ -1751,7 +1834,7 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev) /* HINT method: webcam I2C chips * - * This method work for webcams with Micron sensors + * This method works for webcams with Micron sensors */ static int em28xx_hint_sensor(struct em28xx *dev) { @@ -1761,6 +1844,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; + /* Micron sensor detection */ dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1769,23 +1853,54 @@ static int em28xx_hint_sensor(struct em28xx *dev) return -EINVAL; version = be16_to_cpu(version_be); - switch (version) { - case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; + em28xx_set_model(dev); + sensor_name = "mt9v011"; dev->em28xx_sensor = EM28XX_MT9V011; dev->sensor_xres = 640; dev->sensor_yres = 480; - dev->sensor_xtal = 6300000; + /* + * FIXME: mt9v011 uses I2S speed as xtal clk - at least with + * the Silvercrest cam I have here for testing - for higher + * resolutions, a high clock cause horizontal artifacts, so we + * need to use a lower xclk frequency. + * Yet, it would be possible to adjust xclk depending on the + * desired resolution, since this affects directly the + * frame rate. + */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; + dev->sensor_xtal = 4300000; /* probably means GRGB 16 bit bayer */ dev->vinmode = 0x0d; dev->vinctl = 0x00; break; + + case 0x143a: /* MT9M111 as found in the ECS G200 */ + dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + + sensor_name = "mt9m111"; + dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; + dev->em28xx_sensor = EM28XX_MT9M111; + em28xx_initialize_mt9m111(dev); + dev->sensor_xres = 640; + dev->sensor_yres = 512; + + dev->vinmode = 0x0a; + dev->vinctl = 0x00; + + break; + case 0x8431: dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + sensor_name = "mt9m001"; dev->em28xx_sensor = EM28XX_MT9M001; em28xx_initialize_mt9m001(dev); @@ -1798,10 +1913,13 @@ static int em28xx_hint_sensor(struct em28xx *dev) break; default: - printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); + printk("Unknown Micron Sensor 0x%04x\n", version); return -EINVAL; } + /* Setup webcam defaults */ + em28xx_pre_card_setup(dev); + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); @@ -1813,60 +1931,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) */ void em28xx_pre_card_setup(struct em28xx *dev) { - int rc; - - em28xx_set_model(dev); - - em28xx_info("Identified as %s (card=%d)\n", - dev->board.name, dev->model); - - /* Set the default GPO/GPIO for legacy devices */ - dev->reg_gpo_num = EM2880_R04_GPO; - dev->reg_gpio_num = EM28XX_R08_GPIO; - - dev->wait_after_write = 5; - - /* Based on the Chip ID, set the device configuration */ - rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); - if (rc > 0) { - dev->chip_id = rc; - - switch (dev->chip_id) { - case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); - break; - case CHIP_ID_EM2820: - em28xx_info("chip ID is em2710 or em2820\n"); - break; - case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); - break; - case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); - break; - case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); - dev->wait_after_write = 0; - break; - default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); - } - } - - /* Prepopulate cached GPO register content */ - rc = em28xx_read_reg(dev, dev->reg_gpo_num); - if (rc >= 0) - dev->reg_gpo = rc; - /* Set the initial XCLK and I2C clock values based on the board definition */ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); @@ -1876,9 +1940,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* request some modules */ switch (dev->model) { case EM2861_BOARD_PLEXTOR_PX_TV100U: - /* FIXME guess */ - /* Turn on analog audio output */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + /* Sets the msp34xx I2S speed */ + dev->i2s_speed = 2048000; break; case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: @@ -2164,8 +2227,6 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { - struct i2c_board_info info; - struct IR_i2c_init_data init_data; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2173,50 +2234,51 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + memset(&dev->info, 0, sizeof(&dev->info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); /* detect & configure */ switch (dev->model) { - case (EM2800_BOARD_UNKNOWN): - break; - case (EM2820_BOARD_UNKNOWN): - break; - case (EM2800_BOARD_TERRATEC_CINERGY_200): - case (EM2820_BOARD_TERRATEC_CINERGY_250): - init_data.ir_codes = ir_codes_em_terratec; - init_data.get_key = em28xx_get_key_terratec; - init_data.name = "i2c IR (EM28XX Terratec)"; + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = &ir_codes_em_terratec_table; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; break; - case (EM2820_BOARD_PINNACLE_USB_2): - init_data.ir_codes = ir_codes_pinnacle_grey; - init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; break; - case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): - init_data.ir_codes = ir_codes_hauppauge_new; - init_data.get_key = em28xx_get_key_em_haup; - init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case (EM2820_BOARD_MSI_VOX_USB_2): - break; - case (EM2800_BOARD_LEADTEK_WINFAST_USBII): - break; - case (EM2800_BOARD_KWORLD_USB2800): - break; - case (EM2800_BOARD_GRABBEEX_USB2800): + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; break; } - if (init_data.name) - info.platform_data = &init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + if (dev->init_data.name) + dev->info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); } void em28xx_card_setup(struct em28xx *dev) { - em28xx_set_model(dev); + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) { + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; + else + dev->progressive = 1; + } else + em28xx_set_model(dev); + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); dev->tuner_type = em28xx_boards[dev->model].tuner_type; if (em28xx_boards[dev->model].tuner_addr) @@ -2234,7 +2296,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; -#ifdef CONFIG_MODULES +#if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); #endif /* Call first TVeeprom */ @@ -2248,10 +2310,6 @@ void em28xx_card_setup(struct em28xx *dev) dev->i2s_speed = 2048000; dev->board.has_msp34xx = 1; } -#ifdef CONFIG_MODULES - if (tv.has_ir) - request_module("ir-kbd-i2c"); -#endif break; } case EM2882_BOARD_KWORLD_ATSC_315U: @@ -2290,12 +2348,12 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; - case EM2820_BOARD_SILVERCREST_WEBCAM: - /* FIXME: need to document the registers bellow */ - em28xx_write_reg(dev, 0x0d, 0x42); - em28xx_write_reg(dev, 0x13, 0x08); } +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c && !disable_ir) + request_module("ir-kbd-i2c"); +#endif if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); @@ -2314,60 +2372,62 @@ void em28xx_card_setup(struct em28xx *dev) /* request some modules */ if (dev->board.has_msp34xx) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "msp3400", "msp3400", msp3400_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "msp3400", "msp3400", 0, msp3400_addrs); if (dev->board.decoder == EM28XX_SAA711X) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "saa7115", "saa7115_auto", saa711x_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa7115", "saa7115_auto", 0, saa711x_addrs); if (dev->board.decoder == EM28XX_TVP5150) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvp5150", "tvp5150", tvp5150_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvp5150", "tvp5150", 0, tvp5150_addrs); if (dev->em28xx_sensor == EM28XX_MT9V011) { struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "mt9v011", "mt9v011", 0, mt9v011_addrs); v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); } if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvaudio", "tvaudio", dev->board.tvaudio_addr); + "tvaudio", "tvaudio", dev->board.tvaudio_addr, NULL); if (dev->board.tuner_type != TUNER_ABSENT) { int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); if (dev->board.radio.type) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", "tuner", dev->board.radio_addr); + "tuner", "tuner", dev->board.radio_addr, NULL); if (has_demod) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (dev->tuner_addr == 0) { enum v4l2_i2c_tuner_type type = has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(type)); + 0, v4l2_i2c_tuner_addrs(type)); if (sd) dev->tuner_addr = v4l2_i2c_subdev_addr(sd); } else { v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", "tuner", dev->tuner_addr); + "tuner", "tuner", dev->tuner_addr, NULL); } } em28xx_tuner_setup(dev); - em28xx_ir_init(dev); + + if(!disable_ir) + em28xx_ir_init(dev); } @@ -2433,7 +2493,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { struct em28xx *dev = *devhandle; - int retval = -ENOMEM; + int retval; int errCode; dev->udev = udev; @@ -2450,6 +2510,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + em28xx_set_model(dev); + + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; + + dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ + retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (retval > 0) { + dev->chip_id = retval; + + switch (dev->chip_id) { + case CHIP_ID_EM2710: + em28xx_info("chip ID is em2710\n"); + break; + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820 (or em2710)\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + } + } + + /* Prepopulate cached GPO register content */ + retval = em28xx_read_reg(dev, dev->reg_gpo_num); + if (retval >= 0) + dev->reg_gpo = retval; + em28xx_pre_card_setup(dev); if (!dev->board.is_em2800) { @@ -2482,15 +2594,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, * Default format, used for tvp5150 or saa711x output formats */ dev->vinmode = 0x10; - dev->vinctl = 0x11; - - /* - * If the device can be a webcam, seek for a sensor. - * If sensor is not found, then it isn't a webcam. - */ - if (dev->board.is_webcam) - if (em28xx_hint_sensor(dev) < 0) - dev->board.is_webcam = 0; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); @@ -2509,6 +2614,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); if (dev->board.has_msp34xx) { diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 5b78e199abd..a88257a7d94 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); + /* FIXME */ #define em28xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ @@ -632,6 +636,9 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); + /* enable video capture */ rc = em28xx_write_reg(dev, 0x48, 0x00); @@ -645,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->chip_id == CHIP_ID_EM2860 || + dev->chip_id == CHIP_ID_EM2883) + return 1; + + /* Version of em28xx that does not support VBI */ + return 0; +} + int em28xx_set_outfmt(struct em28xx *dev) { int ret; + u8 vinctrl; ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, dev->format->reg | 0x20, 0xff); @@ -658,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev) if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c); + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -720,13 +751,23 @@ int em28xx_resolution_set(struct em28xx *dev) { int width, height; width = norm_maxw(dev); - height = norm_maxh(dev) >> 1; + height = norm_maxh(dev); + + if (!dev->progressive) + height >>= norm_maxh(dev); em28xx_set_outfmt(dev); em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + + /* If we don't set the start position to 4 in VBI mode, we end up + with line 21 being YUYV encoded instead of being in 8-bit + greyscale */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + else + em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } @@ -838,8 +879,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); */ static void em28xx_irq_callback(struct urb *urb) { - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx *dev = urb->context; int rc, i; switch (urb->status) { @@ -924,6 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int i; int sb_size, pipe; struct urb *urb; @@ -953,7 +994,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } dev->isoc_ctl.max_pkt_size = max_pkt_size; - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + dev->isoc_ctl.vbi_buf = NULL; sb_size = max_packets * dev->isoc_ctl.max_pkt_size; @@ -988,7 +1030,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, usb_fill_int_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); + em28xx_irq_callback, dev, 1); urb->number_of_packets = max_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1003,6 +1045,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } init_waitqueue_head(&dma_q->wq); + init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); @@ -1088,7 +1131,7 @@ struct em28xx *em28xx_get_device(int minor, list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) dev = h; - if (h->vbi_dev->minor == minor) { + if (h->vbi_dev && h->vbi_dev->minor == minor) { dev = h; *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cf0ac7f2a30..db749461e5c 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -33,6 +33,7 @@ #include "s5h1409.h" #include "mt352.h" #include "mt352_priv.h" /* FIXME */ +#include "tda1002x.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -295,6 +296,11 @@ static struct mt352_config terratec_xs_mt352_cfg = { .demod_init = mt352_terratec_xs_init, }; +static struct tda10023_config em28xx_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -478,7 +484,6 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_KWORLD_DVB_310U: - case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); @@ -488,6 +493,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); @@ -549,6 +555,19 @@ static int dvb_init(struct em28xx *dev) } break; #endif + case EM2870_BOARD_REDDO_DVB_C_USB_BOX: + /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ + dvb->frontend = dvb_attach(tda10023_attach, + &em28xx_tda10023_config, + &dev->i2c_adap, 0x48); + if (dvb->frontend) { + if (!dvb_attach(simple_tuner_attach, dvb->frontend, + &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { + result = -EINVAL; + goto out_free; + } + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 27e33a287df..71474d31e15 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -459,7 +459,6 @@ static struct i2c_algorithm em28xx_algo = { static struct i2c_adapter em28xx_adap_template = { .owner = THIS_MODULE, .name = "em28xx", - .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, }; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index a2676d63cfd..ed12e7ffcbd 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -86,7 +86,19 @@ #define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b #define EM28XX_R10_VINMODE 0x10 + #define EM28XX_R11_VINCTRL 0x11 + +/* em28xx Video Input Control Register 0x11 */ +#define EM28XX_VINCTRL_VBI_SLICED 0x80 +#define EM28XX_VINCTRL_VBI_RAW 0x40 +#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ +#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 +#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ +#define EM28XX_VINCTRL_FID_ON_HREF 0x04 +#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 +#define EM28XX_VINCTRL_INTERLACED 0x01 + #define EM28XX_R12_VINENABLE 0x12 /* */ #define EM28XX_R14_GAMMA 0x14 @@ -135,6 +147,10 @@ #define EM28XX_R31_HSCALEHIGH 0x31 #define EM28XX_R32_VSCALELOW 0x32 #define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_R34_VBI_START_H 0x34 +#define EM28XX_R35_VBI_START_V 0x35 +#define EM28XX_R36_VBI_WIDTH 0x36 +#define EM28XX_R37_VBI_HEIGHT 0x37 #define EM28XX_R40_AC97LSB 0x40 #define EM28XX_R41_AC97MSB 0x41 @@ -176,7 +192,8 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { - CHIP_ID_EM2820 = 18, /* Also used by em2710 */ + CHIP_ID_EM2710 = 17, + CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c new file mode 100644 index 00000000000..94943e5a152 --- /dev/null +++ b/drivers/media/video/em28xx/em28xx-vbi.c @@ -0,0 +1,142 @@ +/* + em28xx-vbi.c - VBI driver for em28xx + + Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by EyeMagnet Limited. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "em28xx.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); + +#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + *size = 720 * 12 * 2; + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + int rc = 0; + unsigned int size; + + size = 720 * 12 * 2; + + buf->vb.size = size; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = 720; + buf->vb.height = 12; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops em28xx_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index ff37b4c15f4..3a1dfb7726f 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -124,7 +124,7 @@ static struct em28xx_fmt format[] = { /* supported controls */ /* Common to all boards */ -static struct v4l2_queryctrl em28xx_qctrl[] = { +static struct v4l2_queryctrl ac97_qctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, .type = V4L2_CTRL_TYPE_INTEGER, @@ -133,7 +133,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .maximum = 0x1f, .step = 0x1, .default_value = 0x1f, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -163,7 +163,24 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; do_gettimeofday(&buf->vb.ts); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -194,15 +211,24 @@ static void em28xx_copy_video(struct em28xx *dev, startread = p; remain = len; - /* Interlaces frame */ - if (buf->top_field) + if (dev->progressive) fieldstart = outp; - else - fieldstart = outp + bytesperline; + else { + /* Interlaces two half frames */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + } linesdone = dma_q->pos / bytesperline; currlinedone = dma_q->pos % bytesperline; - offset = linesdone * bytesperline * 2 + currlinedone; + + if (dev->progressive) + offset = linesdone * bytesperline + currlinedone; + else + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; @@ -230,7 +256,8 @@ static void em28xx_copy_video(struct em28xx *dev, if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", + em28xx_isocdbg("Overflow of %zi bytes past buffer end" + "(2)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); lencopy = remain = (char *)outp + buf->vb.size - @@ -247,6 +274,63 @@ static void em28xx_copy_video(struct em28xx *dev, dma_q->pos += len; } +static void em28xx_copy_vbi(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *startwrite, *startread; + int offset; + int bytesperline = 720; + + if (dev == NULL) { + em28xx_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + em28xx_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) { + return; + } + if (p == NULL) { + em28xx_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + em28xx_isocdbg("outp is null\n"); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if ((p[0] == 0x33 && p[1] == 0x95) || + (p[0] == 0x88 && p[1] == 0x88)) { + /* Header field, advance past it */ + p += 4; + } else { + len += 4; + } + + startread = p; + + startwrite = outp + dma_q->pos; + offset = dma_q->pos; + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) { + startwrite += bytesperline * 0x0c; + offset += bytesperline * 0x0c; + } + + memcpy(startwrite, startread, len); + dma_q->pos += len; +} + static inline void print_err_status(struct em28xx *dev, int packet, int status) { @@ -297,7 +381,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -309,7 +393,34 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); - dev->isoc_ctl.buf = *buf; + dev->isoc_ctl.vid_buf = *buf; + + return; +} + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; return; } @@ -320,7 +431,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx_dmaqueue *dma_q = &dev->vidq; unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -337,7 +448,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.buf; + buf = dev->isoc_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -376,7 +487,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], len, (p[2] & 1) ? "odd" : "even"); - if (!(p[2] & 1)) { + if (dev->progressive || !(p[2] & 1)) { if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -401,6 +512,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return rc; } +/* Version of isoc handler that takes into account a mixture of video and + VBI data */ +static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + int vbi_size; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* capture type 0 = vbi start + capture type 1 = video start + capture type 2 = video in progress */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + } + + /* FIXME: get rid of hard-coded value */ + vbi_size = 720 * 0x0c; + + if (dev->capture_type == 0) { + if (dev->vbi_read >= vbi_size) { + /* We've already read all the VBI data, so + treat the rest as video */ + em28xx_isocdbg("dev->vbi_read > vbi_size\n"); + } else if ((dev->vbi_read + len) < vbi_size) { + /* This entire frame is VBI data */ + if (dev->vbi_read == 0 && + (!(dev->cur_field & 1))) { + /* Brand new frame */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + } + + if (dev->vbi_read == 0) { + vbi_dma_q->pos = 0; + if (vbi_buf != NULL) { + if (dev->cur_field & 1) + vbi_buf->top_field = 0; + else + vbi_buf->top_field = 1; + } + } + + dev->vbi_read += len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + } else { + /* Some of this frame is VBI data and some is + video data */ + int vbi_data_len = vbi_size - dev->vbi_read; + dev->vbi_read += vbi_data_len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, vbi_data_len); + dev->capture_type = 1; + p += vbi_data_len; + len -= vbi_data_len; + } + } + + if (dev->capture_type == 1) { + dev->capture_type = 2; + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1) ? "odd" : "even"); + + if (dev->progressive || !(dev->cur_field & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + if (buf != NULL) { + if (dev->cur_field & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL && dev->capture_type == 2) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + + /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -412,7 +670,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) + >> 3; if (0 == *count) *count = EM28XX_DEF_BUF; @@ -449,8 +708,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; + if (dev->isoc_ctl.vid_buf == buf) + dev->isoc_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -466,7 +725,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -485,9 +745,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, dev->max_pkt_size, - em28xx_isoc_copy); + if (em28xx_vbi_supported(dev) == 1) + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy_vbi); + else + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -569,41 +836,89 @@ static void video_mux(struct em28xx *dev, int index) } /* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh) +static int res_get(struct em28xx_fh *fh, unsigned int bit) { struct em28xx *dev = fh->dev; - int rc = 0; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + em28xx_videodbg("res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct em28xx_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct em28xx_fh *fh) +static int res_locked(struct em28xx *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct em28xx_fh *fh) +static void res_free(struct em28xx_fh *fh, unsigned int bits) { struct em28xx *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + em28xx_videodbg("res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct em28xx_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } } /* - * em28xx_get_ctrl() - * return the current saturation, brightness or contrast, mute state + * ac97_queryctrl() + * return the ac97 supported controls */ -static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +static int ac97_queryctrl(struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + /* Control is not ac97 related */ + return 1; +} + +/* + * ac97_get_ctrl() + * return the current values for ac97 mute and volume + */ +static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) { switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -613,29 +928,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) ctrl->value = dev->volume; return 0; default: - return -EINVAL; + /* Control is not ac97 related */ + return 1; } } /* - * em28xx_set_ctrl() - * mute or set new saturation, brightness or contrast + * ac97_set_ctrl() + * set values for ac97 mute and volume */ -static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) { + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) + if (ctrl->id == ac97_qctrl[i].id) + goto handle; + + /* Announce that hasn't handle it */ + return 1; + +handle: + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) + return -ERANGE; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value != dev->mute) { - dev->mute = ctrl->value; - return em28xx_audio_analog_set(dev); - } - return 0; + dev->mute = ctrl->value; + break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); - default: - return -EINVAL; + break; } + + return em28xx_audio_analog_set(dev); } static int check_dev(struct em28xx *dev) @@ -689,7 +1016,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - f->fmt.pix.field = dev->interlaced ? + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; mutex_unlock(&dev->lock); @@ -739,7 +1069,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } else { /* width must even because of the YUYV format height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, + 1, 0); } get_scale(dev, width, height, &hscale, &vscale); @@ -753,7 +1084,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; return 0; } @@ -801,12 +1136,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - if (dev->stream_on && !fh->stream_on) { - em28xx_errdev("%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); @@ -815,6 +1144,21 @@ out: return rc; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + *norm = dev->norm; + + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; @@ -846,6 +1190,41 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) return 0; } +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc = 0; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (dev->board.is_webcam) + rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + video, g_parm, p); + else + v4l2_video_std_frame_period(dev->norm, + &p->parm.capture.timeperframe); + + return rc; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (!dev->board.is_webcam) + return -EINVAL; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); +} + static const char *iname[] = { [EM28XX_VMUX_COMPOSITE1] = "Composite1", [EM28XX_VMUX_COMPOSITE2] = "Composite2", @@ -923,6 +1302,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -964,6 +1346,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -987,7 +1372,6 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int id = qc->id; - int i; int rc; rc = check_dev(dev); @@ -998,15 +1382,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->board.has_msp34xx) { - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); - return 0; - } - } + /* enumberate AC97 controls */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + rc = ac97_queryctrl(qc); + if (!rc) + return 0; } + /* enumberate V4L2 device controls */ mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); mutex_unlock(&dev->lock); @@ -1031,14 +1414,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_get_ctrl(dev, ctrl); + else + rc = 1; + + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - else { - rc = em28xx_get_ctrl(dev, ctrl); - if (rc < 0) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } + rc = 0; } mutex_unlock(&dev->lock); @@ -1050,7 +1435,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u8 i; int rc; rc = check_dev(dev); @@ -1059,28 +1443,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - else { + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_set_ctrl(dev, ctrl); + else rc = 1; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (ctrl->id == em28xx_qctrl[i].id) { - if (ctrl->value < em28xx_qctrl[i].minimum || - ctrl->value > em28xx_qctrl[i].maximum) { - rc = -ERANGE; - break; - } - - rc = em28xx_set_ctrl(dev, ctrl); - break; - } - } - } - /* Control not found - try to send it to the attached devices */ + /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - rc = 0; + + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + rc = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + rc = em28xx_audio_analog_set(dev); + } } mutex_unlock(&dev->lock); @@ -1224,8 +1611,9 @@ static int vidioc_g_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1276,8 +1664,9 @@ static int vidioc_s_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1321,20 +1710,25 @@ static int vidioc_streamon(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; - mutex_lock(&dev->lock); - rc = res_get(fh); + em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - if (likely(rc >= 0)) - rc = videobuf_streamon(&fh->vb_vidq); + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_streamon(&fh->vb_vidq); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1350,17 +1744,22 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - mutex_lock(&dev->lock); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); + em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } return 0; } @@ -1380,9 +1779,14 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + + if (dev->audio_mode.has_audio) + cap->capabilities |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; @@ -1447,6 +1851,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return 0; } +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + format->fmt.vbi.samples_per_line = 720; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + + return 0; +} + +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + format->fmt.vbi.samples_per_line = 720; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + + return 0; +} static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) @@ -1459,7 +1902,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_reqbufs(&fh->vb_vidq, rb); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->vb_vidq, rb); + else + return videobuf_reqbufs(&fh->vb_vbiq, rb); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1473,7 +1919,18 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_querybuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->vb_vidq, b); + else { + /* FIXME: I'm not sure yet whether this is a bug in zvbi or + the videobuf framework, but we probably shouldn't be + returning a buffer larger than that which was asked for. + At a minimum, it causes a crash in zvbi since it does + a memcpy based on the source buffer length */ + int result = videobuf_querybuf(&fh->vb_vbiq, b); + b->length = 17280; + return result; + } } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1486,7 +1943,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_qbuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->vb_vidq, b); + else + return videobuf_qbuf(&fh->vb_vbiq, b); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1499,7 +1959,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & + O_NONBLOCK); + else + return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & + O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1507,7 +1972,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct em28xx_fh *fh = priv; - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + else + return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8); } #endif @@ -1603,9 +2071,9 @@ static int radio_queryctrl(struct file *file, void *priv, qc->id >= V4L2_CID_LASTP1) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); return 0; } } @@ -1624,6 +2092,7 @@ static int em28xx_v4l2_open(struct file *filp) struct em28xx *dev; enum v4l2_buf_type fh_type; struct em28xx_fh *fh; + enum v4l2_field field; dev = em28xx_get_device(minor, &fh_type, &radio); @@ -1665,9 +2134,21 @@ static int em28xx_v4l2_open(struct file *filp) dev->users++; + if (dev->progressive) + field = V4L2_FIELD_NONE; + else + field = V4L2_FIELD_INTERLACED; + videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct em28xx_buffer), fh); + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, field, + sizeof(struct em28xx_buffer), fh); + + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); @@ -1724,20 +2205,21 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_stop(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } if (dev->users == 1) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - /* the device is already disconnect, free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -1758,10 +2240,12 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -1786,16 +2270,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -1813,17 +2303,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } /* @@ -1839,14 +2329,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -1872,6 +2358,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -1884,7 +2372,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, @@ -2003,13 +2494,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->mute = 1; dev->volume = 0x1f; - /* enable vbi capturing */ - /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val)); - em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); em28xx_set_outfmt(dev); em28xx_colorlevels_set_default(dev); @@ -2032,14 +2520,17 @@ int em28xx_register_analog_devices(struct em28xx *dev) } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + if (em28xx_vbi_supported(dev) == 1) { + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + "vbi"); - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("unable to register vbi device\n"); - return ret; + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { @@ -2059,8 +2550,12 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->radio_dev->num); } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", - dev->vdev->num, dev->vbi_dev->num); + em28xx_info("V4L2 video device registered as /dev/video%d\n", + dev->vdev->num); + + if (dev->vbi_dev) + em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n", + dev->vbi_dev->num); return 0; } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 45bd513f62d..0a73e8bf0d6 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -108,6 +108,8 @@ #define EM2882_BOARD_KWORLD_ATSC_315U 69 #define EM2882_BOARD_EVGA_INDTUBE 70 #define EM2820_BOARD_SILVERCREST_WEBCAM 71 +#define EM2861_BOARD_GADMEI_UTV330PLUS 72 +#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -213,7 +215,8 @@ struct em28xx_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct em28xx_buffer *buf; + struct em28xx_buffer *vid_buf; + struct em28xx_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -367,6 +370,7 @@ enum em28xx_sensor { EM28XX_NOSENSOR = 0, EM28XX_MT9V011, EM28XX_MT9M001, + EM28XX_MT9M111, }; enum em28xx_adecoder { @@ -397,6 +401,7 @@ struct em28xx_board { unsigned int has_snapshot_button:1; unsigned int is_webcam:1; unsigned int valid:1; + unsigned int has_ir_i2c:1; unsigned char xclk, i2c_speed; unsigned char radio_addr; @@ -407,7 +412,7 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; - IR_KEYTAB_TYPE *ir_codes; + struct ir_scancode_table *ir_codes; }; struct em28xx_eeprom { @@ -440,6 +445,10 @@ enum em28xx_dev_state { #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +/* em28xx resource types (used for res_get/res_lock etc */ +#define EM28XX_RESOURCE_VIDEO 0x01 +#define EM28XX_RESOURCE_VBI 0x02 + struct em28xx_audio { char name[50]; char *transfer_buffer[EM28XX_AUDIO_BUFS]; @@ -460,10 +469,11 @@ struct em28xx; struct em28xx_fh { struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ int radio; + unsigned int resources; struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -484,10 +494,12 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; + /* Allows progressive (e. g. non-interlaced) mode */ + int progressive; + /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; - unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -538,6 +550,12 @@ struct em28xx { enum em28xx_dev_state state; enum em28xx_io_method io; + /* vbi related state tracking */ + int capture_type; + int vbi_read; + unsigned char cur_field; + + struct work_struct request_module_wk; /* locks */ @@ -549,10 +567,14 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* resources in use */ + unsigned int resources; + unsigned char eedata[256]; /* Isoc control struct */ struct em28xx_dmaqueue vidq; + struct em28xx_dmaqueue vbiq; struct em28xx_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -591,6 +613,10 @@ struct em28xx { struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; + + /* I2C keyboard data */ + struct i2c_board_info info; + struct IR_i2c_init_data init_data; }; struct em28xx_ops { @@ -629,6 +655,7 @@ int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); +int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); @@ -676,6 +703,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev); int em28xx_ir_init(struct em28xx *dev); int em28xx_ir_fini(struct em28xx *dev); +/* Provided by em28xx-vbi.c */ +extern struct videobuf_queue_ops em28xx_vbi_qops; + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index d1c1e457f0b..74092f436be 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1379,8 +1379,10 @@ et61x251_read(struct file* filp, char __user * buf, (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + msecs_to_jiffies( + cam->module_param.frame_timeout * 1000 + ) + ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 34f46f2bc04..fe2e490ebc5 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -19,6 +19,7 @@ if USB_GSPCA && VIDEO_V4L2 source "drivers/media/video/gspca/m5602/Kconfig" source "drivers/media/video/gspca/stv06xx/Kconfig" +source "drivers/media/video/gspca/gl860/Kconfig" config USB_GSPCA_CONEX tristate "Conexant Camera Driver" @@ -47,6 +48,15 @@ config USB_GSPCA_FINEPIX To compile this driver as a module, choose M here: the module will be called gspca_finepix. +config USB_GSPCA_JEILINJ + tristate "Jeilin JPEG USB V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on this Jeilin chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_jeilinj. + config USB_GSPCA_MARS tristate "Mars USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -103,9 +113,9 @@ config USB_GSPCA_PAC7311 module will be called gspca_pac7311. config USB_GSPCA_SN9C20X - tristate "SN9C20X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help Say Y here if you want support for cameras based on the sn9c20x chips (SN9C201 and SN9C202). @@ -113,10 +123,10 @@ config USB_GSPCA_SN9C20X module will be called gspca_sn9c20x. config USB_GSPCA_SN9C20X_EVDEV - bool "Enable evdev support" - depends on USB_GSPCA_SN9C20X - ---help--- - Say Y here in order to enable evdev support for sn9c20x webcam button. + bool "Enable evdev support" + depends on USB_GSPCA_SN9C20X && INPUT + ---help--- + Say Y here in order to enable evdev support for sn9c20x webcam button. config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index f6d3b86e9ad..b7420818037 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o +obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o @@ -30,6 +31,7 @@ gspca_main-objs := gspca.o gspca_conex-objs := conex.o gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o +gspca_jeilinj-objs := jeilinj.o gspca_mars-objs := mars.o gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o @@ -56,3 +58,4 @@ gspca_zc3xx-objs := zc3xx.o obj-$(CONFIG_USB_M5602) += m5602/ obj-$(CONFIG_USB_STV06XX) += stv06xx/ +obj-$(CONFIG_USB_GL860) += gl860/ diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 8d48ea1742c..eca003566ae 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -820,7 +820,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 2c20d06a03e..c1461e63647 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -635,10 +635,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info; if (sd->sensor == SENSOR_PAS106) { cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + cam->nmodes = ARRAY_SIZE(sif_mode); } else { cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); gspca_dev->ctrl_dis = (1 << COLOR_IDX); } sd->brightness = BRIGHTNESS_DEF; diff --git a/drivers/media/video/gspca/gl860/Kconfig b/drivers/media/video/gspca/gl860/Kconfig new file mode 100644 index 00000000000..22772f53ec7 --- /dev/null +++ b/drivers/media/video/gspca/gl860/Kconfig @@ -0,0 +1,8 @@ +config USB_GL860 + tristate "GL860 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the GL860 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_gl860. diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile new file mode 100644 index 00000000000..13c9403cc87 --- /dev/null +++ b/drivers/media/video/gspca/gl860/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_USB_GL860) += gspca_gl860.o + +gspca_gl860-objs := gl860.o \ + gl860-mi1320.o \ + gl860-ov2640.o \ + gl860-ov9655.o \ + gl860-mi2020.o + +EXTRA_CFLAGS += -Idrivers/media/video/gspca + diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c new file mode 100644 index 00000000000..39f6261c1a0 --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c @@ -0,0 +1,537 @@ +/* @file gl860-mi1320.c + * @author Olivier LORIN from my logs + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : MI1320 */ + +#include "gl860.h" + +static struct validx tbl_common[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xffff, 0xffff}, + {0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1}, + {0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1}, + {0xba70, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0xba74, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, + {35, 0xffff}, + {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, + {0x006a, 0x000d}, +}; + +static struct validx tbl_sensor_settings_common[] = { + {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000}, + {0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006}, +}; +static struct validx tbl_sensor_settings_1280[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_800[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_640[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1}, + {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_post_unset_alt[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static u8 *tbl_1280[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1" +}; + +static u8 *tbl_800[] = { + "\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59" +}; + +static u8 *tbl_640[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c" + "\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01" + "\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04" + "\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09" + , + "\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6" + "\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\x00\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1" +}; + +static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d}; +static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70}; +static s32 tbl_backlight[] = {0x0e, 0x06, 0x02}; + +static s32 tbl_cntr1[] = { + 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0}; +static s32 tbl_cntr2[] = { + 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10}; + +static u8 dat_wbalNL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10" + "\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20" + "\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00"; + +static u8 dat_wbalLL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40" + "\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00" + "\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00"; + +static u8 dat_wbalBL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae" + "\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20" + "\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00"; + +static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; + +static u8 s000[] = + "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" + "\xd8\x04\x58\x00\x04\x02"; +static u8 s001[] = + "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" + "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; +static u8 s002[] = + "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" + "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" + "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; +static u8 s003[] = + "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" + "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" + "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; +static u8 s004[] = + "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; +static u8 s005[] = + "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" + "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" + "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; +static u8 s006[] = + "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" + "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" + "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; +static u8 s007[] = + "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" + "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" + "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" + "\xe1\xff\xf1\x00"; +static u8 s008[] = + "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" + "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" + "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; +static u8 s009[] = + "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" + "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" + "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; +static u8 s010[] = + "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" + "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" + "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" + "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; +static u8 s011[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" + "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" + "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev); +static int mi1320_configure_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev); +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev); +static int mi1320_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi1320_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 10; + sd->vcur.gamma = 20; + sd->vcur.hue = 0; + sd->vcur.saturation = 6; + sd->vcur.whitebal = 0; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 2; + sd->vmax.brightness = 8; + sd->vmax.sharpness = 7; + sd->vmax.contrast = 0; /* 10 but not working with tihs driver */ + sd->vmax.gamma = 40; + sd->vmax.hue = 5 + 1; + sd->vmax.saturation = 8; + sd->vmax.whitebal = 2; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + + sd->dev_camera_settings = mi1320_camera_settings; + sd->dev_init_at_startup = mi1320_init_at_startup; + sd->dev_configure_alt = mi1320_configure_alt; + sd->dev_init_pre_alt = mi1320_init_pre_alt; + sd->dev_post_unset_alt = mi1320_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + s32 n; /* reserved for FETCH macros */ + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001); + n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); +} + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + common(gspca_dev); + +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + sd->vold.AC50Hz = -1; + + common(gspca_dev); + + mi1320_sensor_settings(gspca_dev); + + mi1320_init_post_alt(gspca_dev); + + return 0; +} + +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev) +{ + mi1320_camera_settings(gspca_dev); + + return 0; +} + +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_sensor_settings_common, + ARRAY_SIZE(tbl_sensor_settings_common)); + + switch (reso) { + case IMAGE_1280: + fetch_validx(gspca_dev, tbl_sensor_settings_1280, + ARRAY_SIZE(tbl_sensor_settings_1280)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]); + break; + + case IMAGE_800: + fetch_validx(gspca_dev, tbl_sensor_settings_800, + ARRAY_SIZE(tbl_sensor_settings_800)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]); + break; + + default: + fetch_validx(gspca_dev, tbl_sensor_settings_640, + ARRAY_SIZE(tbl_sensor_settings_640)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]); + break; + } + return 0; +} + +static int mi1320_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +int mi1320_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = sd->vcur.hue; + s32 sat = sd->vcur.saturation; + s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + s32 i; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + freq = 2 * (freq == 0); + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + for (i = 0; i < 2; i++) { + if (wbal == 0) { /* Normal light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalNL); + } + + if (wbal == 1) { /* Low light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0004, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0043, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalLL); + } + + if (wbal == 2) { /* Back light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 44, dat_wbalBL); + } + } + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + bright = tbl_bright[bright]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + sat = tbl_sat[sat]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL); + } + + if (hue != sd->vold.hue) { + /* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */ + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + if (hue == sd->vmax.hue) + sd->swapRB = 1; + else + sd->swapRB = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + backlight = tbl_backlight[backlight]; + for (i = 0; i < 2; i++) { + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1, + 0, NULL); + } + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00}; + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[3] = flip + 2 * mirror; + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2); + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + gam = 2 * gam; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1, + 0, NULL); + } + + return 0; +} + +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c new file mode 100644 index 00000000000..ffb09fed3e8 --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -0,0 +1,937 @@ +/* @file gl860-mi2020.c + * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's + * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : MI2020 */ + +#include "gl860.h" + +static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; +static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; +static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; +static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_bright6[] = {0x90, 0x00, 0x05}; + +static u8 dat_dummy1[] = {0x90, 0x00, 0x06}; +/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/ +/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/ + +static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; +static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; +static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; + +static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; + +static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; +static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; + +static struct validx tbl_common_a[] = { + {0x0000, 0x0000}, + {1, 0xffff}, /* msleep(35); */ + {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, + {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8}, + {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, +}; + +static struct validx tbl_common_b[] = { + {0x006a, 0x0007}, + {35, 0xffff}, + {0x00ef, 0x0006}, + {35, 0xffff}, + {0x006a, 0x000d}, + {35, 0xffff}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, + {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, +}; + +static struct idxdata tbl_common_c[] = { + {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, + {6, "\xff\xff\xff"}, /* 12 */ + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, /* - */ + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"}, + {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"}, + {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"}, + {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"}, + {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"}, + {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"}, + {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"}, + {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, + {1, "\xff\xff\xff"}, + {0x33, "\x78\x00\x00"}, + {1, "\xff\xff\xff"}, + {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, + {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, + {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, + {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, + {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, +}; + +static struct idxdata tbl_common_d[] = { + {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, + {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, + {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, + {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"}, + {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"}, + {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, +}; + +static struct idxdata tbl_common_e[] = { + {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, + {0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, + /* msleep(53); */ + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, + {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, + {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, + {0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, + {0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, + {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, + {0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, + {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, + {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, + {0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, + {0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, + {0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, + {0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, + {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, + {0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, + {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, + {0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, + {0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, + {0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, + {0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, + {0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, + {0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, + {0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, + {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, + {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, + {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, + {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, + {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, + {0x33, "\x8c\x24\x15"}, +}; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, + {53, 0xffff}, + {0x0010, 0x0010}, + {53, 0xffff}, + {0x0008, 0x00c0}, + {53, 0xffff}, + {0x0001, 0x00c1}, + {53, 0xffff}, + {0x0001, 0x00c2}, + {53, 0xffff}, + {0x0020, 0x0006}, + {53, 0xffff}, + {0x006a, 0x000d}, + {53, 0xffff}, +}; + +static struct idxdata tbl_init_post_alt_low_a[] = { + {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, + {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, + {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, + {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"}, + {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"}, + {0x33, "\x90\x00\x9b"}, +}; + +static struct idxdata tbl_init_post_alt_low_b[] = { + {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low_c[] = { + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {2, "\xff\xff\xff"}, /* - * */ + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {1, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low_d[] = { + {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, + /* Flip/Mirror h/v=1 */ + {0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, + {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x06"}, + {130, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, + {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, + {100, "\xff\xff\xff"}, + /* ?? */ + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + /* Brigthness=70 */ + {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + /* Sharpness=20 */ + {0x32, "\x6c\x14\x08"}, +}; + +static struct idxdata tbl_init_post_alt_big_a[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"}, + {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, + {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, +}; + +static struct idxdata tbl_init_post_alt_big_b[] = { + {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, +}; + +static struct idxdata tbl_init_post_alt_big_c[] = { + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, +}; + +static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; +static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; +static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; +static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41"; + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev); +static int mi2020_configure_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev); +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi2020_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi2020_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 70; + sd->vcur.sharpness = 20; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 60; + sd->vcur.whitebal = 50; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 128; + sd->vmax.sharpness = 40; + sd->vmax.contrast = 3; + sd->vmax.gamma = 2; + sd->vmax.hue = 0 + 1; /* 200 */ + sd->vmax.saturation = 0; /* 100 */ + sd->vmax.whitebal = 0; /* 100 */ + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + if (_MI2020b_) { + sd->vmax.contrast = 0; + sd->vmax.gamma = 0; + sd->vmax.backlight = 0; + } + + sd->dev_camera_settings = mi2020_camera_settings; + sd->dev_init_at_startup = mi2020_init_at_startup; + sd->dev_configure_alt = mi2020_configure_alt; + sd->dev_init_pre_alt = mi2020_init_pre_alt; + sd->dev_post_unset_alt = mi2020_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + if (_MI2020b_) { + fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a)); + } else { + if (_MI2020_) + ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); + else + ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); + msleep(35); + fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b)); + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); + msleep(2); /* - * */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); + if (reso == IMAGE_1600) + msleep(2); /* 1600 */ + fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c)); + + if (_MI2020b_ || _MI2020_) + fetch_idxdata(gspca_dev, tbl_common_d, + ARRAY_SIZE(tbl_common_d)); + + fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e)); + if (_MI2020b_ || _MI2020_) { + /* Different from fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); + /* Same as fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); + /* Different from fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90"); + } else { + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80"); + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05"); + msleep(2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + if (reso == IMAGE_1600) + msleep(14); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06"); + msleep(2); +} + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) +{ + u8 c; + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + common(gspca_dev); + + return 0; +} + +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + sd->vold.AC50Hz = -1; + + mi2020_init_post_alt(gspca_dev); + + return 0; +} + +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + s32 backlight = sd->vcur.backlight; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 c; + + sd->nbIm = -1; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = backlight; + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + msleep(200); + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + msleep(3); /* 35 * */ + + common(gspca_dev); + + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + msleep(70); + + if (_MI2020b_) + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + + switch (reso) { + case IMAGE_640: + case IMAGE_800: + if (reso != IMAGE_800) + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_640); + else + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_800); + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a, + ARRAY_SIZE(tbl_init_post_alt_low_a)); + + if (reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b, + ARRAY_SIZE(tbl_init_post_alt_low_b)); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c, + ARRAY_SIZE(tbl_init_post_alt_low_c)); + + if (_MI2020b_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(150); + } else if (_MI2020c_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } else if (_MI2020_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + /* backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + /* at init time but not after */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); + /* finish the backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(5);/* " */ + + if (_MI2020c_) { + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d, + ARRAY_SIZE(tbl_init_post_alt_low_d)); + } else { + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + msleep(14); /* 0xd8 */ + + /* flip/mirror */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip6); + msleep(21); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + /* end of flip/mirror main part */ + msleep(246); /* 146 */ + + sd->nbIm = 0; + } + break; + + case IMAGE_1280: + case IMAGE_1600: + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x05\x04"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\x02"); + } else { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x06\x40"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\xb0"); + } + + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a, + ARRAY_SIZE(tbl_init_post_alt_big_a)); + + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00"); + msleep(53); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + msleep(53); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + msleep(53); + + if (_MI2020b_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + if (reso == IMAGE_1600) + msleep(500); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + } else if (_MI2020c_ || _MI2020_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + /* backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + /* at init time but not after */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); + /* finish the backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(6); /* " */ + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + msleep(14); + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b, + ARRAY_SIZE(tbl_init_post_alt_big_b)); + + /* flip/mirror */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + /* end of flip/mirror main part */ + msleep(16); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + if (reso == IMAGE_1600) + msleep(25); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + msleep(103); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + sd->nbIm = 0; + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c, + ARRAY_SIZE(tbl_init_post_alt_big_c)); + } + + sd->vold.mirror = mirror; + sd->vold.flip = flip; + sd->vold.AC50Hz = freq; + sd->vold.backlight = backlight; + + mi2020_camera_settings(gspca_dev); + + return 0; +} + +static int mi2020_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +int mi2020_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = (sd->vcur.hue > 0); + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + + u8 dat_sharp[] = {0x6c, 0x00, 0x08}; + u8 dat_bright2[] = {0x90, 0x00, 0x00}; + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + + /* Less than 4 images received -> too early to set the settings */ + if (sd->nbIm < 4) { + sd->waitSet = 1; + return 0; + } + sd->waitSet = 0; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(130); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + + /* Sometimes present, sometimes not, useful? */ + /* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/ + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = backlight; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + dat_multi1[2] = 0x6d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = 0x40 + gam; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + dat_multi1[2] = 0x6d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright2[2] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + dat_sharp[1] = sharp; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp); + } + + if (hue != sd->vold.hue) { + sd->swapRB = hue; + sd->vold.hue = hue; + } + + return 0; +} + +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(20); + if (_MI2020c_ || _MI2020_) + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); + else + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); +} diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c new file mode 100644 index 00000000000..14b9c373f9f --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c @@ -0,0 +1,505 @@ +/* @file gl860-ov2640.c + * @author Olivier LORIN, from Malmostoso's logs + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : OV2640 */ + +#include "gl860.h" + +static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; +static u8 dat_init2[] = {0x61}; /* expected */ +static u8 dat_init3[] = {0x51}; /* expected */ + +static u8 dat_post[] = + "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; + +static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81"; +static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; +static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; +static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; + +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006}, + {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, + {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, + {0x0041, 0x0000}, {0x0061, 0x0000}, +}; + +static struct validx tbl_common[] = { + {0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff}, + {0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013}, + {0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b}, + {0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039}, + {0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023}, + {0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007}, + {0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a}, + {0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025}, + {0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061}, + {0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c}, + {0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073}, + {0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050}, + {0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b}, + {0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076}, + {0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c}, + {0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9}, + {0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044}, + {0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8}, + {0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d}, + {0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091}, + {0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091}, + {0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091}, + {0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091}, + {0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093}, + {0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093}, + {0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097}, + {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097}, + {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097}, + {0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4}, + {0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7}, + {0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9}, + {0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0}, + {0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050}, + {0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, + {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b}, + {0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da}, + {0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, + {0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, +}; + +static struct validx tbl_sensor_settings_common_a[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, + {50, 0xffff}, + {0x0061, 0x0000}, + {0xffff, 0xffff}, + {0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d}, + {30, 0xffff}, + {0x0040, 0x0000}, +}; + +static struct validx tbl_sensor_settings_common_b[] = { + {0x6001, 0x00ff}, {0x6038, 0x000c}, + {10, 0xffff}, + {0x6000, 0x0011}, + /* backlight=31/64 */ + {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, + /* bright=0/256 */ + {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d}, + /* wbal=64/128 */ + {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d}, + /* cntr=0/256 */ + {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d}, + /* sat=128/256 */ + {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d}, + /* sharpness=0/32 */ + {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093}, + /* hue=0/256 */ + {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d}, + /* gam=32/64 */ + {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d}, + /* image right up */ + {0xffff, 0xffff}, + {15, 0xffff}, + {0x6001, 0x00ff}, {0x6000, 0x8004}, + {0xffff, 0xffff}, + {0x60a8, 0x0004}, + {15, 0xffff}, + {0x6001, 0x00ff}, {0x6000, 0x8004}, + {0xffff, 0xffff}, + {0x60f8, 0x0004}, + /* image right up */ + {0xffff, 0xffff}, + /* backlight=31/64 */ + {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, +}; + +static struct validx tbl_640[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, + {0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d}, + {0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086}, + {0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, + {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a}, + {0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x60a1, 0x005a}, +}; + +static struct validx tbl_800[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032}, + {0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d}, + {0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020}, + {0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1}, + {0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, +}; + +static struct validx tbl_big_a[] = { + {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, + {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f}, + {0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f}, + {0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, +}; + +static struct validx tbl_big_b[] = { + {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, + {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, + {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, + {0x6000, 0x008e}, +}; + +static struct validx tbl_big_c[] = { + {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, + {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, + {0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093}, + {0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087}, + {0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, + {0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff}, + {0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e}, + {0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014}, + {0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, +}; + +static struct validx tbl_post_unset_alt[] = { + {0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000}, + {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d}, + {50, 0xffff}, + {0x0021, 0x0000}, +}; + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev); +static int ov2640_configure_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev); +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov2640_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov2640_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 32; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 0; + sd->vcur.gamma = 32; + sd->vcur.hue = 0; + sd->vcur.saturation = 128; + sd->vcur.whitebal = 64; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 31; + sd->vmax.contrast = 255; + sd->vmax.gamma = 64; + sd->vmax.hue = 255 + 1; + sd->vmax.saturation = 255; + sd->vmax.whitebal = 128; + sd->vmax.mirror = 0; + sd->vmax.flip = 0; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov2640_camera_settings; + sd->dev_init_at_startup = ov2640_init_at_startup; + sd->dev_configure_alt = ov2640_configure_alt; + sd->dev_init_pre_alt = ov2640_init_pre_alt; + sd->dev_post_unset_alt = ov2640_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); +} + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1); + + common(gspca_dev); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2); + + ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3); + + ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + + ov2640_init_post_alt(gspca_dev); + + return 0; +} + +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH macros */ + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a, + ARRAY_SIZE(tbl_sensor_settings_common_a)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); + common(gspca_dev); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a, + ARRAY_SIZE(tbl_sensor_settings_common_a), n); + + switch (reso) { + case IMAGE_640: + n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640); + break; + + case IMAGE_800: + n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); + break; + + case IMAGE_1600: + case IMAGE_1280: + n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a)); + + if (reso == IMAGE_1280) { + n = fetch_validx(gspca_dev, tbl_big_b, + ARRAY_SIZE(tbl_big_b)); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); + } + + n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c)); + + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + } + break; + } + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b)); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + + ov2640_camera_settings(gspca_dev); + + return 0; +} + +static int ov2640_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov2640_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 gam = sd->vcur.gamma; + s32 cntr = sd->vcur.contrast; + s32 sat = sd->vcur.saturation; + s32 hue = sd->vcur.hue; + s32 wbal = sd->vcur.whitebal; + + if (backlight != sd->vold.backlight) { + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + 0, NULL); + /* No sd->vold.backlight=backlight; (to be done again later) */ + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, + 0, NULL); + if (hue >= sd->vmax.hue) + sd->swapRB = 1; + else + sd->swapRB = 0; + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + 0, NULL); + } + + return 0; +} + +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(20); + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c new file mode 100644 index 00000000000..eda3346f939 --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -0,0 +1,337 @@ +/* @file gl860-ov9655.c + * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt + * on dsd's weblog + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : OV9655 */ + +#include "gl860.h" + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + + {0x0040, 0x0000}, +}; + +static struct validx tbl_commmon[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000}, + {0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000}, +}; + +static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12}; + +static u8 *tbl_640[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61" + "\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00" + "\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7" + "\x4d\xe7\x4e\xe7" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x8d\x90\x7c\x91\x7b" + , + "\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" +}; + +static u8 *tbl_800[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" + "\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb" + "\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8" + "\x4d\xe8\x4e\xe8" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x0d\x90\x90\x91\x90" + , + "\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00" +}; + +static u8 c04[] = {0x04}; +static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post_2[] = "\x10\x10\xc1\x02"; +static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post_4[] = "\x10\x02\xc1\x06"; +static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post_6[] = "\x10\x10\xc1\x05"; +static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; + +static struct validx tbl_init_post_alt[] = { + {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, + {0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, +}; + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev); +static int ov9655_configure_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev); +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov9655_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov9655_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 128; + sd->vcur.sharpness = 0; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 0; + sd->vcur.whitebal = 0; + + sd->vmax.backlight = 0; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 0; + sd->vmax.contrast = 0; + sd->vmax.gamma = 0; + sd->vmax.hue = 0 + 1; + sd->vmax.saturation = 0; + sd->vmax.whitebal = 0; + sd->vmax.mirror = 0; + sd->vmax.flip = 0; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov9655_camera_settings; + sd->dev_init_at_startup = ov9655_init_at_startup; + sd->dev_configure_alt = ov9655_configure_alt; + sd->dev_init_pre_alt = ov9655_init_pre_alt; + sd->dev_post_unset_alt = ov9655_post_unset_alt; +} + +/*==========================================================================*/ + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/ + + return 0; +} + +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vold.brightness = -1; + sd->vold.hue = -1; + + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); + + ov9655_init_post_alt(gspca_dev); + + return 0; +} + +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH macros */ + s32 i; + u8 **tbl; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800; + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[0], tbl[0]); + for (i = 1; i < 7; i++) + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, + tbl_length[i], tbl[i]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[7], tbl[7]); + + n = fetch_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt)); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8); + + ov9655_camera_settings(gspca_dev); + + return 0; +} + +static int ov9655_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 1 + 1; + break; + + default: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov9655_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70"; + + s32 bright = sd->vcur.brightness; + s32 hue = sd->vcur.hue; + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright[3] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + sd->swapRB = (hue != 0); + } + + return 0; +} + +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL); +} diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c new file mode 100644 index 00000000000..6ef59ac7f50 --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -0,0 +1,785 @@ +/* @file gl860.c + * @date 2009-08-27 + * + * Genesys Logic webcam with gl860 subdrivers + * + * Driver by Olivier Lorin <o.lorin@laposte.net> + * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> + * Thanks BUGabundo and Malmostoso for your amazing help! + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ +#include "gspca.h" +#include "gl860.h" + +MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>"); +MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/*======================== static function declarations ====================*/ + +static void (*dev_init_settings)(struct gspca_dev *gspca_dev); + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); +static int sd_init(struct gspca_dev *gspca_dev); +static int sd_isoc_init(struct gspca_dev *gspca_dev); +static int sd_start(struct gspca_dev *gspca_dev); +static void sd_stop0(struct gspca_dev *gspca_dev); +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, u8 *data, s32 len); +static void sd_callback(struct gspca_dev *gspca_dev); + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + s32 vendor_id, s32 product_id); + +/*============================ driver options ==============================*/ + +static s32 AC50Hz = 0xff; +module_param(AC50Hz, int, 0644); +MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); + +static char sensor[7]; +module_param_string(sensor, sensor, sizeof(sensor), 0644); +MODULE_PARM_DESC(sensor, + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')"); + +/*============================ webcam controls =============================*/ + +/* Functions to get and set a control value */ +#define SD_SETGET(thename) \ +static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + sd->vcur.thename = val;\ + if (gspca_dev->streaming)\ + sd->dev_camera_settings(gspca_dev);\ + return 0;\ +} \ +static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + *val = sd->vcur.thename;\ + return 0;\ +} + +SD_SETGET(mirror) +SD_SETGET(flip) +SD_SETGET(AC50Hz) +SD_SETGET(backlight) +SD_SETGET(brightness) +SD_SETGET(gamma) +SD_SETGET(hue) +SD_SETGET(saturation) +SD_SETGET(sharpness) +SD_SETGET(whitebal) +SD_SETGET(contrast) + +#define GL860_NCTRLS 11 + +/* control table */ +static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; +static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; +static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; + +#define SET_MY_CTRL(theid, \ + thetype, thelabel, thename) \ + if (sd->vmax.thename != 0) {\ + sd_ctrls[nCtrls].qctrl.id = theid;\ + sd_ctrls[nCtrls].qctrl.type = thetype;\ + strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ + sd_ctrls[nCtrls].qctrl.minimum = 0;\ + sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ + sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ + sd_ctrls[nCtrls].qctrl.step = \ + (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ + sd_ctrls[nCtrls].set = sd_set_##thename;\ + sd_ctrls[nCtrls].get = sd_get_##thename;\ + nCtrls++;\ + } + +static int gl860_build_control_table(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct ctrl *sd_ctrls; + int nCtrls = 0; + + if (_MI1320_) + sd_ctrls = sd_ctrls_mi1320; + else if (_MI2020_) + sd_ctrls = sd_ctrls_mi2020; + else if (_MI2020b_) + sd_ctrls = sd_ctrls_mi2020b; + else if (_OV2640_) + sd_ctrls = sd_ctrls_ov2640; + else if (_OV9655_) + sd_ctrls = sd_ctrls_ov9655; + else + return 0; + + memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); + + SET_MY_CTRL(V4L2_CID_BRIGHTNESS, + V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) + SET_MY_CTRL(V4L2_CID_SHARPNESS, + V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) + SET_MY_CTRL(V4L2_CID_CONTRAST, + V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) + SET_MY_CTRL(V4L2_CID_GAMMA, + V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) + SET_MY_CTRL(V4L2_CID_HUE, + V4L2_CTRL_TYPE_INTEGER, "Palette", hue) + SET_MY_CTRL(V4L2_CID_SATURATION, + V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) + SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, + V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) + SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, + V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) + + SET_MY_CTRL(V4L2_CID_HFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) + SET_MY_CTRL(V4L2_CID_VFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) + SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz) + + return nCtrls; +} + +/*==================== sud-driver structure initialisation =================*/ + +static struct sd_desc sd_desc_mi1320 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi1320, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_mi2020 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi2020, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_mi2020b = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi2020b, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_ov2640 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov2640, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_ov9655 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov9655, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +/*=========================== sub-driver image sizes =======================*/ + +static struct v4l2_pix_format mi2020_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format ov2640_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format mi1320_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, +}; + +static struct v4l2_pix_format ov9655_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, +}; + +/*========================= sud-driver functions ===========================*/ + +/* This function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + s32 vendor_id, product_id; + + /* Get USB VendorID and ProductID */ + vendor_id = le16_to_cpu(id->idVendor); + product_id = le16_to_cpu(id->idProduct); + + sd->nbRightUp = 1; + sd->nbIm = -1; + + sd->sensor = 0xff; + if (strcmp(sensor, "MI1320") == 0) + sd->sensor = ID_MI1320; + else if (strcmp(sensor, "OV2640") == 0) + sd->sensor = ID_OV2640; + else if (strcmp(sensor, "OV9655") == 0) + sd->sensor = ID_OV9655; + else if (strcmp(sensor, "MI2020") == 0) + sd->sensor = ID_MI2020; + else if (strcmp(sensor, "MI2020b") == 0) + sd->sensor = ID_MI2020b; + + /* Get sensor and set the suitable init/start/../stop functions */ + if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) + return -1; + + cam = &gspca_dev->cam; + gspca_dev->nbalt = 4; + + switch (sd->sensor) { + case ID_MI1320: + gspca_dev->sd_desc = &sd_desc_mi1320; + cam->cam_mode = mi1320_mode; + cam->nmodes = ARRAY_SIZE(mi1320_mode); + dev_init_settings = mi1320_init_settings; + break; + + case ID_MI2020: + gspca_dev->sd_desc = &sd_desc_mi2020; + cam->cam_mode = mi2020_mode; + cam->nmodes = ARRAY_SIZE(mi2020_mode); + dev_init_settings = mi2020_init_settings; + break; + + case ID_MI2020b: + gspca_dev->sd_desc = &sd_desc_mi2020b; + cam->cam_mode = mi2020_mode; + cam->nmodes = ARRAY_SIZE(mi2020_mode); + dev_init_settings = mi2020_init_settings; + break; + + case ID_OV2640: + gspca_dev->sd_desc = &sd_desc_ov2640; + cam->cam_mode = ov2640_mode; + cam->nmodes = ARRAY_SIZE(ov2640_mode); + dev_init_settings = ov2640_init_settings; + break; + + case ID_OV9655: + gspca_dev->sd_desc = &sd_desc_ov9655; + cam->cam_mode = ov9655_mode; + cam->nmodes = ARRAY_SIZE(ov9655_mode); + dev_init_settings = ov9655_init_settings; + break; + } + + dev_init_settings(gspca_dev); + if (AC50Hz != 0xff) + ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; + gl860_build_control_table(gspca_dev); + + return 0; +} + +/* This function is called at probe time after sd_config */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_at_startup(gspca_dev); +} + +/* This function is called before to choose the alt setting */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_configure_alt(gspca_dev); +} + +/* This function is called to start the webcam */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_pre_alt(gspca_dev); +} + +/* This function is called to stop the webcam */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_post_unset_alt(gspca_dev); +} + +/* This function is called when an image is being received */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, u8 *data, s32 len) +{ + struct sd *sd = (struct sd *) gspca_dev; + static s32 nSkipped; + + s32 mode = (s32) gspca_dev->curr_mode; + s32 nToSkip = + sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); + + /* Test only against 0202h, so endianess does not matter */ + switch (*(s16 *) data) { + case 0x0202: /* End of frame, start a new one */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + nSkipped = 0; + if (sd->nbIm >= 0 && sd->nbIm < 10) + sd->nbIm++; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + break; + + default: + data += 2; + len -= 2; + if (nSkipped + len <= nToSkip) + nSkipped += len; + else { + if (nSkipped < nToSkip && nSkipped + len > nToSkip) { + data += nToSkip - nSkipped; + len -= nToSkip - nSkipped; + nSkipped = nToSkip + 1; + } + gspca_frame_add(gspca_dev, + INTER_PACKET, frame, data, len); + } + break; + } +} + +/* This function is called when an image has been read */ +/* This function is used to monitor webcam orientation */ +static void sd_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!_OV9655_) { + u8 state; + u8 upsideDown; + + /* Probe sensor orientation */ + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); + + /* C8/40 means upside-down (looking backwards) */ + /* D8/50 means right-up (looking onwards) */ + upsideDown = (state == 0xc8 || state == 0x40); + + if (upsideDown && sd->nbRightUp > -4) { + if (sd->nbRightUp > 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == -3) { + sd->mirrorMask = 1; + sd->waitSet = 1; + } + sd->nbRightUp--; + } + if (!upsideDown && sd->nbRightUp < 4) { + if (sd->nbRightUp < 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == 3) { + sd->mirrorMask = 0; + sd->waitSet = 1; + } + sd->nbRightUp++; + } + } + + if (sd->waitSet) + sd->dev_camera_settings(gspca_dev); +} + +/*=================== USB driver structure initialisation ==================*/ + +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x05e3, 0x0503)}, + {USB_DEVICE(0x05e3, 0xf191)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct gspca_dev *gspca_dev; + s32 ret; + + ret = gspca_dev_probe(intf, id, + &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); + + if (ret >= 0) { + gspca_dev = usb_get_intfdata(intf); + + PDEBUG(D_PROBE, + "Camera is now controlling video device /dev/video%d", + gspca_dev->vdev.minor); + } + + return ret; +} + +static void sd_disconnect(struct usb_interface *intf) +{ + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/*====================== Init and Exit module functions ====================*/ + +static int __init sd_mod_init(void) +{ + PDEBUG(D_PROBE, "driver startup - version %s", DRIVER_VERSION); + + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "driver registered"); + + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "driver deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +/*==========================================================================*/ + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata) +{ + struct usb_device *udev = gspca_dev->dev; + s32 r = 0; + + if (pref == 0x40) { /* Send */ + if (len > 0) { + memcpy(gspca_dev->usb_buf, pdata, len); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + } else { + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } else { /* Receive */ + if (len > 0) { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + memcpy(pdata, gspca_dev->usb_buf, len); + } else { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } + + if (r < 0) + PDEBUG(D_ERR, + "ctrl transfer failed %4d " + "[p%02x r%d v%04x i%04x len%d]", + r, pref, req, val, index, len); + else if (len > 1 && r < len) + PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); + + if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index)) + msleep(1); + if (_OV2640_) + msleep(1); + + return r; +} + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, + tbl[n].idx, 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n) +{ + while (++n < len) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, + 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, + 3, tbl[n].data); + else + msleep(tbl[n].idx); + } +} + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + s32 vendor_id, s32 product_id) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 probe, nb26, nb96, nOV, ntry; + + if (product_id == 0xf191) + sd->sensor = ID_MI1320; + + if (sd->sensor == 0xff) { + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + msleep(56); + + nOV = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + msleep(10); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); + PDEBUG(D_PROBE, "1st probe=%02x", probe); + if (probe == 0xff) + nOV++; + } + + if (nOV) { + PDEBUG(D_PROBE, "0xff -> sensor OVXXXX"); + PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655"); + + nb26 = nb96 = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, + 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, + 0, NULL); + msleep(10); + /* Wait for 26(OV2640) or 96(OV9655) */ + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, + 1, &probe); + + PDEBUG(D_PROBE, "2nd probe=%02x", probe); + if (probe == 0x00) + nb26++; + if (probe == 0x26 || probe == 0x40) { + sd->sensor = ID_OV2640; + nb26 += 4; + break; + } + if (probe == 0x96 || probe == 0x55) { + sd->sensor = ID_OV9655; + nb96 += 4; + break; + } + if (probe == 0xff) + nb96++; + msleep(3); + } + if (nb26 < 4 && nb96 < 4) { + PDEBUG(D_PROBE, "No relevant answer "); + PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655"); + PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640"); + PDEBUG(D_PROBE, + "To force a sensor, add that line to " + "/etc/modprobe.d/options.conf:"); + PDEBUG(D_PROBE, "options gspca_gl860 " + "sensor=\"OV2640\" or \"OV9655\""); + return -1; + } + } else { /* probe = 0 */ + PDEBUG(D_PROBE, "No 0xff -> sensor MI2020"); + sd->sensor = ID_MI2020; + } + } + + if (_MI1320_) { + PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); + } else if (_MI2020_) { + PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); + } else if (_MI2020b_) { + PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)"); + } else if (_OV9655_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); + } else if (_OV2640_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); + } else { + PDEBUG(D_PROBE, "***** Unknown sensor *****"); + return -1; + } + + return 0; +} diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h new file mode 100644 index 00000000000..cef4e24c1e6 --- /dev/null +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -0,0 +1,108 @@ +/* @file gl860.h + * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef GL860_DEV_H +#define GL860_DEV_H +#include <linux/version.h> + +#include "gspca.h" + +#define MODULE_NAME "gspca_gl860" +#define DRIVER_VERSION "0.9d10" + +#define ctrl_in gl860_RTx +#define ctrl_out gl860_RTx + +#define ID_MI1320 1 +#define ID_OV2640 2 +#define ID_OV9655 4 +#define ID_MI2020 8 +#define ID_MI2020b 16 + +#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) +#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) +#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b) +#define _MI2020c_ 0 +#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) +#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) + +#define IMAGE_640 0 +#define IMAGE_800 1 +#define IMAGE_1280 2 +#define IMAGE_1600 3 + +struct sd_gl860 { + u16 backlight; + u16 brightness; + u16 sharpness; + u16 contrast; + u16 gamma; + u16 hue; + u16 saturation; + u16 whitebal; + u8 mirror; + u8 flip; + u8 AC50Hz; +}; + +/* Specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct sd_gl860 vcur; + struct sd_gl860 vold; + struct sd_gl860 vmax; + + int (*dev_configure_alt) (struct gspca_dev *); + int (*dev_init_at_startup)(struct gspca_dev *); + int (*dev_init_pre_alt) (struct gspca_dev *); + void (*dev_post_unset_alt) (struct gspca_dev *); + int (*dev_camera_settings)(struct gspca_dev *); + + u8 swapRB; + u8 mirrorMask; + u8 sensor; + s32 nbIm; + s32 nbRightUp; + u8 waitSet; +}; + +struct validx { + u16 val; + u16 idx; +}; + +struct idxdata { + u8 idx; + u8 data[3]; +}; + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len); +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n); +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len); + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata); + +void mi1320_init_settings(struct gspca_dev *); +void ov2640_init_settings(struct gspca_dev *); +void ov9655_init_settings(struct gspca_dev *); +void mi2020_init_settings(struct gspca_dev *); + +#endif diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index b8561dfb6c8..cf6540da1e4 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 6, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -486,6 +486,7 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); + gspca_dev->alt = i; /* memorize the current alt setting */ if (gspca_dev->nbalt > 1) { ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { @@ -493,7 +494,6 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) return NULL; } } - gspca_dev->alt = i; /* memorize the current alt setting */ return ep; } @@ -512,7 +512,10 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (!gspca_dev->cam.bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (gspca_dev->pkt_size == 0) + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + else + psize = gspca_dev->pkt_size; npkt = gspca_dev->cam.npkt; if (npkt == 0) npkt = 32; /* default value */ @@ -597,13 +600,18 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { + ret = gspca_dev->sd_desc->isoc_init(gspca_dev); + if (ret < 0) + goto out; + } + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ep = get_ep(gspca_dev); - if (ep == NULL) { - ret = -EIO; - goto out; - } ret = create_urbs(gspca_dev, ep); if (ret < 0) goto out; @@ -628,21 +636,32 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); - if (ret < 0) { - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb [%d] err %d", n, ret); - gspca_dev->streaming = 0; - destroy_urbs(gspca_dev); - if (ret == -ENOSPC) { - msleep(20); /* wait for kill - * complete */ - break; /* try the previous alt */ - } - goto out; - } + if (ret < 0) + break; } if (ret >= 0) break; + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); + gspca_dev->streaming = 0; + destroy_urbs(gspca_dev); + if (ret != -ENOSPC) + goto out; + + /* the bandwidth is not wide enough + * negociate or try a lower alternate setting */ + msleep(20); /* wait for kill complete */ + if (gspca_dev->sd_desc->isoc_nego) { + ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); + if (ret < 0) + goto out; + } else { + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } + } } out: mutex_unlock(&gspca_dev->usb_lock); @@ -1473,12 +1492,6 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -static int vidioc_s_std(struct file *filp, void *priv, - v4l2_std_id *parm) -{ - return 0; -} - #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) @@ -1949,7 +1962,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, - .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 46c4effdfcd..70b1fd83087 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -98,9 +98,11 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ - cam_op start; /* called on stream on */ + cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ + cam_op isoc_init; /* called on stream on before getting the EP */ + cam_op isoc_nego; /* called when URB submit failed with NOSPC */ cam_v_op stopN; /* called on stream off - main alt */ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ @@ -178,6 +180,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ + u16 pkt_size; /* ISOC packet size */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c new file mode 100644 index 00000000000..a11c97ebeb0 --- /dev/null +++ b/drivers/media/video/gspca/jeilinj.c @@ -0,0 +1,390 @@ +/* + * Jeilinj subdriver + * + * Supports some Jeilin dual-mode cameras which use bulk transport and + * download raw JPEG data. + * + * Copyright (C) 2009 Theodore Kilgore + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "jeilinj" + +#include <linux/workqueue.h> +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>"); +MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define JEILINJ_CMD_TIMEOUT 500 +#define JEILINJ_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define JEILINJ_MAX_TRANSFER 0x200 + +#define FRAME_HEADER_LEN 0x10 + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + const struct v4l2_pix_format *cap_mode; + /* Driver stuff */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; + u8 quality; /* image quality */ + u8 jpegqual; /* webcam quality */ + u8 *jpeg_hdr; +}; + + struct jlj_command { + unsigned char instruction[2]; + unsigned char ack_wanted; + }; + +/* AFAICT these cameras will only do 320x240. */ +static struct v4l2_pix_format jlj_mode[] = { + { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0} +}; + +/* + * cam uses endpoint 0x03 to send commands, 0x84 for read commands, + * and 0x82 for bulk transfer. + */ + +/* All commands are two bytes only */ +static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) +{ + int retval; + + memcpy(gspca_dev->usb_buf, command, 2); + retval = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, 2, NULL, 500); + if (retval < 0) + PDEBUG(D_ERR, "command write [%02x] error %d", + gspca_dev->usb_buf[0], retval); + return retval; +} + +/* Responses are one byte only */ +static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) +{ + int retval; + + retval = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x84), + gspca_dev->usb_buf, 1, NULL, 500); + response = gspca_dev->usb_buf[0]; + if (retval < 0) + PDEBUG(D_ERR, "read command [%02x] error %d", + gspca_dev->usb_buf[0], retval); + return retval; +} + +static int jlj_start(struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + u8 response = 0xff; + struct jlj_command start_commands[] = { + {{0x71, 0x81}, 0}, + {{0x70, 0x05}, 0}, + {{0x95, 0x70}, 1}, + {{0x71, 0x81}, 0}, + {{0x70, 0x04}, 0}, + {{0x95, 0x70}, 1}, + {{0x71, 0x00}, 0}, + {{0x70, 0x08}, 0}, + {{0x95, 0x70}, 1}, + {{0x94, 0x02}, 0}, + {{0xde, 0x24}, 0}, + {{0x94, 0x02}, 0}, + {{0xdd, 0xf0}, 0}, + {{0x94, 0x02}, 0}, + {{0xe3, 0x2c}, 0}, + {{0x94, 0x02}, 0}, + {{0xe4, 0x00}, 0}, + {{0x94, 0x02}, 0}, + {{0xe5, 0x00}, 0}, + {{0x94, 0x02}, 0}, + {{0xe6, 0x2c}, 0}, + {{0x94, 0x03}, 0}, + {{0xaa, 0x00}, 0}, + {{0x71, 0x1e}, 0}, + {{0x70, 0x06}, 0}, + {{0x71, 0x80}, 0}, + {{0x70, 0x07}, 0} + }; + for (i = 0; i < ARRAY_SIZE(start_commands); i++) { + retval = jlj_write2(gspca_dev, start_commands[i].instruction); + if (retval < 0) + return retval; + if (start_commands[i].ack_wanted) + retval = jlj_read1(gspca_dev, response); + if (retval < 0) + return retval; + } + PDEBUG(D_ERR, "jlj_start retval is %d", retval); + return retval; +} + +static int jlj_stop(struct gspca_dev *gspca_dev) +{ + int i; + int retval; + struct jlj_command stop_commands[] = { + {{0x71, 0x00}, 0}, + {{0x70, 0x09}, 0}, + {{0x71, 0x80}, 0}, + {{0x70, 0x05}, 0} + }; + for (i = 0; i < ARRAY_SIZE(stop_commands); i++) { + retval = jlj_write2(gspca_dev, stop_commands[i].instruction); + if (retval < 0) + return retval; + } + return retval; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface the gspca usb_lock is + * used when performing the one USB control operation inside the workqueue, + * which tells the camera to close the stream. In practice the only thing + * which needs to be protected against is the usb_set_interface call that + * gspca makes during stream_off. Otherwise the camera doesn't provide any + * controls that the user could try to change. + */ + +static void jlj_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct gspca_frame *frame; + int blocks_left; /* 0x200-sized blocks remaining in current frame. */ + int size_in_blocks; + int act_len; + int discarding = 0; /* true if we failed to get space for frame. */ + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + PDEBUG(D_ERR, "Couldn't allocate USB buffer"); + goto quit_stream; + } + while (gspca_dev->present && gspca_dev->streaming) { + if (!gspca_dev->present) + goto quit_stream; + /* Start a new frame, and add the JPEG header, first thing */ + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + dev->jpeg_hdr, JPEG_HDR_SZ); + else + discarding = 1; + /* + * Now request data block 0. Line 0 reports the size + * to download, in blocks of size 0x200, and also tells the + * "actual" data size, in bytes, which seems best to ignore. + */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, JEILINJ_MAX_TRANSFER, &act_len, + JEILINJ_DATA_TIMEOUT); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for Block 0", + act_len, JEILINJ_MAX_TRANSFER); + if (ret < 0 || act_len < FRAME_HEADER_LEN) + goto quit_stream; + size_in_blocks = buffer[0x0a]; + blocks_left = buffer[0x0a] - 1; + PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); + packet_type = INTER_PACKET; + if (frame && !discarding) + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, packet_type, + frame, buffer + FRAME_HEADER_LEN, + JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); + else + discarding = 1; + while (blocks_left > 0) { + if (!gspca_dev->present) + goto quit_stream; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, JEILINJ_MAX_TRANSFER, &act_len, + JEILINJ_DATA_TIMEOUT); + if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER) + goto quit_stream; + PDEBUG(D_STREAM, + "%d blocks remaining for frame", blocks_left); + blocks_left -= 1; + if (blocks_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + if (frame && !discarding) + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, + JEILINJ_MAX_TRANSFER); + else + discarding = 1; + } + } +quit_stream: + mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->present) + jlj_stop(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + + dev->quality = 85; + dev->jpegqual = 85; + PDEBUG(D_PROBE, + "JEILINJ camera detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + cam->cam_mode = jlj_mode; + cam->nmodes = 1; + cam->bulk = 1; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 32; + INIT_WORK(&dev->work_struct, jlj_dostream); + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for jlj_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); + kfree(dev->jpeg_hdr); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + /* create the JPEG header */ + dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (dev->jpeg_hdr == NULL) + return -ENOMEM; + jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(dev->jpeg_hdr, dev->quality); + PDEBUG(D_STREAM, "Start streaming at 320x240"); + ret = jlj_start(gspca_dev); + if (ret < 0) { + PDEBUG(D_ERR, "Start streaming command failed"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* Table of supported USB devices */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0979, 0x0280)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 8a5bba16ff3..7f1e5415850 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -56,7 +56,7 @@ int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) return (err < 0) ? err : 0; } -/* Writes a byte to to the m5602 */ +/* Writes a byte to the m5602 */ int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) { int err; diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c index 7aafeb7cfa0..2a28b74cb3f 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.c +++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c @@ -20,6 +20,18 @@ static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); const static struct ctrl ov7660_ctrls[] = { #define GAIN_IDX 1 @@ -37,6 +49,79 @@ const static struct ctrl ov7660_ctrls[] = { .set = ov7660_set_gain, .get = ov7660_get_gain }, +#define BLUE_BALANCE_IDX 2 +#define RED_BALANCE_IDX 3 +#define AUTO_WHITE_BALANCE_IDX 4 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_white_balance, + .get = ov7660_get_auto_white_balance + }, +#define AUTO_GAIN_CTRL_IDX 5 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto gain control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_gain, + .get = ov7660_get_auto_gain + }, +#define AUTO_EXPOSURE_IDX 6 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_exposure, + .get = ov7660_get_auto_exposure + }, +#define HFLIP_IDX 7 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov7660_set_hflip, + .get = ov7660_get_hflip + }, +#define VFLIP_IDX 8 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov7660_set_vflip, + .get = ov7660_get_vflip + }, + }; static struct v4l2_pix_format ov7660_modes[] = { @@ -137,7 +222,7 @@ int ov7660_init(struct sd *sd) } else { data[0] = init_ov7660[i][2]; err = m5602_write_sensor(sd, - init_ov7660[i][1], data, 1); + init_ov7660[i][1], data, 1); } } @@ -148,6 +233,28 @@ int ov7660_init(struct sd *sd) if (err < 0) return err; + err = ov7660_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov7660_set_auto_gain(&sd->gspca_dev, + sensor_settings[AUTO_GAIN_CTRL_IDX]); + if (err < 0) + return err; + + err = ov7660_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); + if (err < 0) + return err; + err = ov7660_set_hflip(&sd->gspca_dev, + sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = ov7660_set_vflip(&sd->gspca_dev, + sensor_settings[VFLIP_IDX]); + return err; } @@ -194,6 +301,159 @@ static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) return err; } + +static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + return 0; +} + +static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto white balance to %d", val); + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); + err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); + + return err; +} + +static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; + PDEBUG(D_V4L2, "Read auto gain control %d", *val); + return 0; +} + +static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto gain control to %d", val); + + sensor_settings[AUTO_GAIN_CTRL_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); + + return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); +} + +static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read auto exposure control %d", *val); + return 0; +} + +static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto exposure control to %d", val); + + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); + + return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); +} + +static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + return 0; +} + +static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + + sensor_settings[HFLIP_IDX] = val; + + i2c_data = ((val & 0x01) << 5) | + (sensor_settings[VFLIP_IDX] << 4); + + err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); + + return err; +} + +static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return 0; +} + +static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + sensor_settings[VFLIP_IDX] = val; + + i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); + err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); + if (err < 0) + return err; + + /* When vflip is toggled we need to readjust the bridge hsync/vsync */ + if (gspca_dev->streaming) + err = ov7660_start(sd); + + return err; +} + static void ov7660_dump_registers(struct sd *sd) { int address; diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h index 3f2c169a93e..f5588ebe667 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.h +++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h @@ -66,23 +66,23 @@ #define OV7660_RBIAS 0x2c #define OV7660_HREF 0x32 #define OV7660_ADC 0x37 -#define OV7660_OFON 0x39 -#define OV7660_TSLB 0x3a -#define OV7660_COM12 0x3c -#define OV7660_COM13 0x3d +#define OV7660_OFON 0x39 +#define OV7660_TSLB 0x3a +#define OV7660_COM12 0x3c +#define OV7660_COM13 0x3d #define OV7660_LCC1 0x62 #define OV7660_LCC2 0x63 #define OV7660_LCC3 0x64 #define OV7660_LCC4 0x65 #define OV7660_LCC5 0x66 -#define OV7660_HV 0x69 -#define OV7660_RSVDA1 0xa1 +#define OV7660_HV 0x69 +#define OV7660_RSVDA1 0xa1 #define OV7660_DEFAULT_GAIN 0x0e -#define OV7660_DEFAULT_RED_GAIN 0x80 +#define OV7660_DEFAULT_RED_GAIN 0x80 #define OV7660_DEFAULT_BLUE_GAIN 0x80 #define OV7660_DEFAULT_SATURATION 0x00 -#define OV7660_DEFAULT_EXPOSURE 0x20 +#define OV7660_DEFAULT_EXPOSURE 0x20 /* Kernel module parameters */ extern int force_sensor; @@ -149,45 +149,8 @@ static const unsigned char init_ov7660[][4] = {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - - {SENSOR, OV7660_OFON, 0x0c}, - {SENSOR, OV7660_COM2, 0x11}, - {SENSOR, OV7660_COM7, 0x05}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - - {SENSOR, OV7660_AECH, OV7660_DEFAULT_EXPOSURE}, - {SENSOR, OV7660_COM1, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, @@ -196,11 +159,8 @@ static const unsigned char init_ov7660[][4] = {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - {SENSOR, OV7660_COM7, 0x80}, {SENSOR, OV7660_CLKRC, 0x80}, - {SENSOR, OV7660_BLUE_GAIN, 0x80}, - {SENSOR, OV7660_RED_GAIN, 0x80}, {SENSOR, OV7660_COM9, 0x4c}, {SENSOR, OV7660_OFON, 0x43}, {SENSOR, OV7660_COM12, 0x28}, @@ -212,17 +172,17 @@ static const unsigned char init_ov7660[][4] = {SENSOR, OV7660_PSHFT, 0x0b}, {SENSOR, OV7660_VSTART, 0x01}, {SENSOR, OV7660_VSTOP, 0x7a}, - {SENSOR, OV7660_VREF, 0x00}, + {SENSOR, OV7660_VSTOP, 0x00}, {SENSOR, OV7660_COM7, 0x05}, - {SENSOR, OV7660_COM6, 0x4b}, - {SENSOR, OV7660_BBIAS, 0x98}, - {SENSOR, OV7660_GbBIAS, 0x98}, - {SENSOR, OV7660_RSVD29, 0x98}, - {SENSOR, OV7660_RBIAS, 0x98}, + {SENSOR, OV7660_COM6, 0x42}, + {SENSOR, OV7660_BBIAS, 0x94}, + {SENSOR, OV7660_GbBIAS, 0x94}, + {SENSOR, OV7660_RSVD29, 0x94}, + {SENSOR, OV7660_RBIAS, 0x94}, {SENSOR, OV7660_COM1, 0x00}, {SENSOR, OV7660_AECH, 0x00}, {SENSOR, OV7660_AECHH, 0x00}, - {SENSOR, OV7660_ADC, 0x04}, + {SENSOR, OV7660_ADC, 0x05}, {SENSOR, OV7660_COM13, 0x00}, {SENSOR, OV7660_RSVDA1, 0x23}, {SENSOR, OV7660_TSLB, 0x0d}, @@ -233,6 +193,47 @@ static const unsigned char init_ov7660[][4] = {SENSOR, OV7660_LCC4, 0x40}, {SENSOR, OV7660_LCC5, 0x01}, + {SENSOR, OV7660_AECH, 0x20}, + {SENSOR, OV7660_COM1, 0x00}, + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + {SENSOR, OV7660_AECH, 0x5f}, + {SENSOR, OV7660_COM1, 0x03}, + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, @@ -245,35 +246,18 @@ static const unsigned char init_ov7660[][4] = {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, {BRIDGE, M5602_XB_SIG_INI, 0x00}, {BRIDGE, M5602_XB_SIG_INI, 0x02}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x27}, /* 39 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xa7}, /* 679 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x27}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7}, {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - - {SENSOR, OV7660_AECH, 0x20}, - {SENSOR, OV7660_COM1, 0x00}, - {SENSOR, OV7660_OFON, 0x0c}, - {SENSOR, OV7660_COM2, 0x11}, - {SENSOR, OV7660_COM7, 0x05}, - {SENSOR, OV7660_BLUE_GAIN, 0x80}, - {SENSOR, OV7660_RED_GAIN, 0x80}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0} }; #endif diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 0163903d1c0..59400e85896 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -47,6 +47,12 @@ static DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") } }, { + .ident = "Fujitsu-Siemens Amilo Pa 2548", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548") + } + }, { .ident = "MSI GX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), @@ -54,6 +60,13 @@ static DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") } }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/19/2007") + } + }, { .ident = "MSI GX700/GX705/EX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 7127321ace8..6b89f33a4ce 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -178,8 +178,10 @@ sensor_found: sens_priv->settings = kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); - if (!sens_priv->settings) + if (!sens_priv->settings) { + kfree(sens_priv); return -ENOMEM; + } sd->gspca_dev.cam.cam_mode = s5k83a_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 30132513400..140c8f320e4 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -3,6 +3,21 @@ * * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> * + * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ + * and for the routines for detecting and classifying these various cameras, + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> + * + * Acknowledgements: + * + * The MR97311A support in gspca/mars.c has been helpful in understanding some + * of the registers in these cameras. + * + * Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> + * have assisted with their experience. Each of them has also helped by + * testing a previously unsupported camera. + * * 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 @@ -22,18 +37,108 @@ #include "gspca.h" -MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>"); +#define CAM_TYPE_CIF 0 +#define CAM_TYPE_VGA 1 + +#define MR97310A_BRIGHTNESS_MIN -254 +#define MR97310A_BRIGHTNESS_MAX 255 +#define MR97310A_BRIGHTNESS_DEFAULT 0 + +#define MR97310A_EXPOSURE_MIN 300 +#define MR97310A_EXPOSURE_MAX 4095 +#define MR97310A_EXPOSURE_DEFAULT 1000 + +#define MR97310A_GAIN_MIN 0 +#define MR97310A_GAIN_MAX 31 +#define MR97310A_GAIN_DEFAULT 25 + +MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>," + "Theodore Kilgore <kilgota@auburn.edu>"); MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); MODULE_LICENSE("GPL"); +/* global parameters */ +int force_sensor_type = -1; +module_param(force_sensor_type, int, 0644); +MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ u8 sof_read; + u8 cam_type; /* 0 is CIF and 1 is VGA */ + u8 sensor_type; /* We use 0 and 1 here, too. */ + u8 do_lcd_stop; + + int brightness; + u16 exposure; + u8 gain; +}; + +struct sensor_w_data { + u8 reg; + u8 flags; + u8 data[16]; + int len; }; +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static void setbrightness(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); + /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { + { +#define BRIGHTNESS_IDX 0 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = MR97310A_BRIGHTNESS_MIN, + .maximum = MR97310A_BRIGHTNESS_MAX, + .step = 1, + .default_value = MR97310A_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define EXPOSURE_IDX 1 + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = MR97310A_EXPOSURE_MIN, + .maximum = MR97310A_EXPOSURE_MAX, + .step = 1, + .default_value = MR97310A_EXPOSURE_DEFAULT, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { +#define GAIN_IDX 2 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = MR97310A_GAIN_MIN, + .maximum = MR97310A_GAIN_MAX, + .step = 1, + .default_value = MR97310A_GAIN_DEFAULT, + .flags = 0, + }, + .set = sd_setgain, + .get = sd_getgain, + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -65,7 +170,7 @@ static const struct v4l2_pix_format vga_mode[] = { }; /* the bytes to write are in gspca_dev->usb_buf */ -static int reg_w(struct gspca_dev *gspca_dev, int len) +static int mr_write(struct gspca_dev *gspca_dev, int len) { int rc; @@ -78,15 +183,249 @@ static int reg_w(struct gspca_dev *gspca_dev, int len) return rc; } +/* the bytes are read into gspca_dev->usb_buf */ +static int mr_read(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, len, NULL, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg read [%02x] error %d", + gspca_dev->usb_buf[0], rc); + return rc; +} + +static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, + const u8 *data, int len) +{ + gspca_dev->usb_buf[0] = 0x1f; + gspca_dev->usb_buf[1] = flags; + gspca_dev->usb_buf[2] = reg; + memcpy(gspca_dev->usb_buf + 3, data, len); + + return mr_write(gspca_dev, len + 3); +} + +static int sensor_write_regs(struct gspca_dev *gspca_dev, + const struct sensor_w_data *data, int len) +{ + int i, rc; + + for (i = 0; i < len; i++) { + rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, + data[i].data, data[i].len); + if (rc < 0) + return rc; + } + + return 0; +} + +static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 buf, confirm_reg; + int rc; + + buf = data; + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + if (rc < 0) + return rc; + + buf = 0x01; + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); + if (rc < 0) + return rc; + + return 0; +} + +static int cam_get_response16(struct gspca_dev *gspca_dev) +{ + __u8 *data = gspca_dev->usb_buf; + int err_code; + + data[0] = 0x21; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + return err_code; +} + +static int zero_the_pointer(struct gspca_dev *gspca_dev) +{ + __u8 *data = gspca_dev->usb_buf; + int err_code; + u8 status = 0; + int tries = 0; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = mr_write(gspca_dev, 1); + data[0] = 0x19; + data[1] = 0x51; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0xba; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + while (status != 0x0a && tries < 256) { + err_code = cam_get_response16(gspca_dev); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + if (status != 0x0a) + PDEBUG(D_ERR, "status is %02x", status); + + tries = 0; + while (tries < 4) { + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + + data[0] = 0x19; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + return 0; +} + +static u8 get_sensor_id(struct gspca_dev *gspca_dev) +{ + int err_code; + + gspca_dev->usb_buf[0] = 0x1e; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]); + + return gspca_dev->usb_buf[0]; +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + __u8 *data = gspca_dev->usb_buf; + int err_code; cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + + if (id->idProduct == 0x010e) { + sd->cam_type = CAM_TYPE_CIF; + cam->nmodes--; + + data[0] = 0x01; + data[1] = 0x01; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + msleep(200); + data[0] = get_sensor_id(gspca_dev); + /* + * Known CIF cameras. If you have another to report, please do + * + * Name byte just read sd->sensor_type + * reported by + * Sakar Spy-shot 0x28 T. Kilgore 0 + * Innovage 0xf5 (unstable) T. Kilgore 0 + * Vivitar Mini 0x53 H. De Goede 0 + * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0 + * Vivitar Mini 0x08 T. Kilgore 1 + * Elta-Media 8212dc 0x23 T. Kaiser 1 + * Philips dig. keych. 0x37 T. Kilgore 1 + */ + if ((data[0] & 0x78) == 8 || + ((data[0] & 0x2) == 0x2 && data[0] != 0x53)) + sd->sensor_type = 1; + else + sd->sensor_type = 0; + + PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", + sd->sensor_type); + + if (force_sensor_type != -1) { + sd->sensor_type = !! force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + + if (sd->sensor_type == 0) + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + } else { + sd->cam_type = CAM_TYPE_VGA; + PDEBUG(D_PROBE, "MR97310A VGA camera detected"); + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) | + (1 << EXPOSURE_IDX) | (1 << GAIN_IDX); + } + + sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; + sd->exposure = MR97310A_EXPOSURE_DEFAULT; + sd->gain = MR97310A_GAIN_DEFAULT; + return 0; } @@ -96,183 +435,462 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int start_cif_cam(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 *data = gspca_dev->usb_buf; int err_code; - - sd->sof_read = 0; - - /* Note: register descriptions guessed from MR97113A driver */ - + const __u8 startup_string[] = { + 0x00, + 0x0d, + 0x01, + 0x00, /* Hsize/8 for 352 or 320 */ + 0x00, /* Vsize/4 for 288 or 240 */ + 0x13, /* or 0xbb, depends on sensor */ + 0x00, /* Hstart, depends on res. */ + 0x00, /* reserved ? */ + 0x00, /* Vstart, depends on res. and sensor */ + 0x50, /* 0x54 to get 176 or 160 */ + 0xc0 + }; + + /* Note: Some of the above descriptions guessed from MR97113A driver */ data[0] = 0x01; data[1] = 0x01; - err_code = reg_w(gspca_dev, 2); + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x00; - data[1] = 0x0d; - data[2] = 0x01; - data[5] = 0x2b; - data[7] = 0x00; - data[9] = 0x50; /* reg 8, no scale down */ - data[10] = 0xc0; + memcpy(data, startup_string, 11); + if (sd->sensor_type) + data[5] = 0xbb; switch (gspca_dev->width) { case 160: - data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ /* fall thru */ case 320: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall thru */ - case 640: default: - data[3] = 0x50; /* reg 2, H size */ - data[4] = 0x78; /* reg 3, V size */ - data[6] = 0x04; /* reg 5, H start */ - data[8] = 0x03; /* reg 7, V start */ + data[3] = 0x28; /* reg 2, H size/8 */ + data[4] = 0x3c; /* reg 3, V size/4 */ + data[6] = 0x14; /* reg 5, H start */ + data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ break; - case 176: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ + data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ /* fall thru */ case 352: - data[3] = 0x2c; /* reg 2, H size */ - data[4] = 0x48; /* reg 3, V size */ - data[6] = 0x94; /* reg 5, H start */ - data[8] = 0x63; /* reg 7, V start */ + data[3] = 0x2c; /* reg 2, H size/8 */ + data[4] = 0x48; /* reg 3, V size/4 */ + data[6] = 0x06; /* reg 5, H start */ + data[8] = 0x06 + sd->sensor_type; /* reg 7, V start */ break; } - - err_code = reg_w(gspca_dev, 11); + err_code = mr_write(gspca_dev, 11); if (err_code < 0) return err_code; - data[0] = 0x0a; - data[1] = 0x80; - err_code = reg_w(gspca_dev, 2); + if (!sd->sensor_type) { + const struct sensor_w_data cif_sensor0_init_data[] = { + {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, + 0x0f, 0x14, 0x0f, 0x10}, 8}, + {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, + {0x12, 0x00, {0x07}, 1}, + {0x1f, 0x00, {0x06}, 1}, + {0x27, 0x00, {0x04}, 1}, + {0x29, 0x00, {0x0c}, 1}, + {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, + {0x50, 0x00, {0x60}, 1}, + {0x60, 0x00, {0x06}, 1}, + {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, + {0x72, 0x00, {0x1e, 0x56}, 2}, + {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, + 0x31, 0x80, 0x00}, 9}, + {0x11, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, + ARRAY_SIZE(cif_sensor0_init_data)); + } else { /* sd->sensor_type = 1 */ + const struct sensor_w_data cif_sensor1_init_data[] = { + /* Reg 3,4, 7,8 get set by the controls */ + {0x02, 0x00, {0x10}, 1}, + {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ + {0x06, 0x01, {0x00}, 1}, + {0x09, 0x02, {0x0e}, 1}, + {0x0a, 0x02, {0x05}, 1}, + {0x0b, 0x02, {0x05}, 1}, + {0x0c, 0x02, {0x0f}, 1}, + {0x0d, 0x02, {0x07}, 1}, + {0x0e, 0x02, {0x0c}, 1}, + {0x0f, 0x00, {0x00}, 1}, + {0x10, 0x00, {0x06}, 1}, + {0x11, 0x00, {0x07}, 1}, + {0x12, 0x00, {0x00}, 1}, + {0x13, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, + ARRAY_SIZE(cif_sensor1_init_data)); + } if (err_code < 0) return err_code; - data[0] = 0x14; - data[1] = 0x0a; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + setbrightness(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); - data[0] = 0x1b; - data[1] = 0x00; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + msleep(200); - data[0] = 0x15; - data[1] = 0x16; - err_code = reg_w(gspca_dev, 2); + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x16; - data[1] = 0x10; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + return 0; +} - data[0] = 0x17; - data[1] = 0x3a; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; +static int start_vga_cam(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, + 0x00, 0x00, 0x00, 0x50, 0xc0}; - data[0] = 0x18; - data[1] = 0x68; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + /* What some of these mean is explained in start_cif_cam(), above */ + sd->sof_read = 0; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x02; - data[3] = 0x06; - data[4] = 0x59; - data[5] = 0x0c; - data[6] = 0x16; - data[7] = 0x00; - data[8] = 0x07; - data[9] = 0x00; - data[10] = 0x01; - err_code = reg_w(gspca_dev, 11); + /* + * We have to know which camera we have, because the register writes + * depend upon the camera. This test, run before we actually enter + * the initialization routine, distinguishes most of the cameras, If + * needed, another routine is done later, too. + */ + memset(data, 0, 16); + data[0] = 0x20; + err_code = mr_write(gspca_dev, 1); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + err_code = mr_read(gspca_dev, 16); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x0a; - data[3] = 0x00; - data[4] = 0x01; - data[5] = 0x00; - data[6] = 0x00; - data[7] = 0x01; - data[8] = 0x00; - data[9] = 0x0a; - err_code = reg_w(gspca_dev, 10); - if (err_code < 0) - return err_code; + PDEBUG(D_PROBE, "Byte reported is %02x", data[0]); + + msleep(200); + /* + * Known VGA cameras. If you have another to report, please do + * + * Name byte just read sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0x31 0 1 + * ION digital 0x31 0 1 + * Argus DC-1620 0x30 1 0 + * Argus QuickClix 0x30 1 1 (not caught here) + */ + sd->sensor_type = data[0] & 1; + sd->do_lcd_stop = (~data[0]) & 1; + + - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + /* Streaming setup begins here. */ + + + data[0] = 0x01; + data[1] = 0x01; + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x12; - data[3] = 0x00; - data[4] = 0x63; - data[5] = 0x00; - data[6] = 0x70; - data[7] = 0x00; - data[8] = 0x00; - err_code = reg_w(gspca_dev, 9); + /* + * A second test can now resolve any remaining ambiguity in the + * identification of the camera type, + */ + if (!sd->sensor_type) { + data[0] = get_sensor_id(gspca_dev); + if (data[0] == 0x7f) { + sd->sensor_type = 1; + PDEBUG(D_PROBE, "sensor_type corrected to 1"); + } + msleep(200); + } + + if (force_sensor_type != -1) { + sd->sensor_type = !! force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + + /* + * Known VGA cameras. + * This test is only run if the previous test returned 0x30, but + * here is the information for all others, too, just for reference. + * + * Name byte just read sd->sensor_type + * + * Aiptek Pencam VGA+ 0xfb (this test not run) 1 + * ION digital 0xbd (this test not run) 1 + * Argus DC-1620 0xe5 (no change) 0 + * Argus QuickClix 0x7f (reclassified) 1 + */ + memcpy(data, startup_string, 11); + if (!sd->sensor_type) { + data[5] = 0x00; + data[10] = 0x91; + } + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + /* fall thru */ + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 640: + default: + data[3] = 0x50; /* reg 2, H size/8 */ + data[4] = 0x78; /* reg 3, V size/4 */ + data[6] = 0x04; /* reg 5, H start */ + data[8] = 0x03; /* reg 7, V start */ + if (sd->do_lcd_stop) + data[8] = 0x04; /* Bayer tile shifted */ + break; + + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size */ + data[4] = 0x48; /* reg 3, V size */ + data[6] = 0x94; /* reg 5, H start */ + data[8] = 0x63; /* reg 7, V start */ + if (sd->do_lcd_stop) + data[8] = 0x64; /* Bayer tile shifted */ + break; + } + + err_code = mr_write(gspca_dev, 11); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + if (!sd->sensor_type) { + /* The only known sensor_type 0 cam is the Argus DC-1620 */ + const struct sensor_w_data vga_sensor0_init_data[] = { + {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, + {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, + {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, + {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, + {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, + ARRAY_SIZE(vga_sensor0_init_data)); + } else { /* sd->sensor_type = 1 */ + const struct sensor_w_data vga_sensor1_init_data[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + 0x07, 0x00, 0x01}, 8}, + {0x11, 0x04, {0x01}, 1}, + /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */ + {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, + 0x00, 0x0a}, 7}, + {0x11, 0x04, {0x01}, 1}, + {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, + {0x11, 0x04, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, + ARRAY_SIZE(vga_sensor1_init_data)); + } if (err_code < 0) return err_code; + msleep(200); data[0] = 0x00; data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = reg_w(gspca_dev, 2); + err_code = mr_write(gspca_dev, 2); + + return err_code; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err_code; + struct cam *cam; + + cam = &gspca_dev->cam; + sd->sof_read = 0; + /* + * Some of the supported cameras require the memory pointer to be + * set to 0, or else they will not stream. + */ + zero_the_pointer(gspca_dev); + msleep(200); + if (sd->cam_type == CAM_TYPE_CIF) { + err_code = start_cif_cam(gspca_dev); + } else { + err_code = start_vga_cam(gspca_dev); + } return err_code; } static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int result; gspca_dev->usb_buf[0] = 1; gspca_dev->usb_buf[1] = 0; - result = reg_w(gspca_dev, 2); + result = mr_write(gspca_dev, 2); if (result < 0) PDEBUG(D_ERR, "Camera Stop failed"); + + /* Not all the cams need this, but even if not, probably a good idea */ + zero_the_pointer(gspca_dev); + if (sd->do_lcd_stop) { + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + result = mr_write(gspca_dev, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + return; + + /* Note register 7 is also seen as 0x8x or 0xCx in dumps */ + if (sd->brightness > 0) { + sensor_write1(gspca_dev, 7, 0x00); + val = sd->brightness; + } else { + sensor_write1(gspca_dev, 7, 0x01); + val = 257 - sd->brightness; + } + sensor_write1(gspca_dev, 8, val); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) + return; + + if (sd->sensor_type) { + val = sd->exposure >> 4; + sensor_write1(gspca_dev, 3, val); + val = sd->exposure & 0xf; + sensor_write1(gspca_dev, 4, val); + } else { + u8 clockdiv; + int exposure; + + /* We have both a clock divider and an exposure register. + We first calculate the clock divider, as that determines + the maximum exposure and then we calculayte the exposure + register setting (which goes from 0 - 511). + + Note our 0 - 4095 exposure is mapped to 0 - 511 + milliseconds exposure time */ + clockdiv = (60 * sd->exposure + 7999) / 8000; + + /* Limit framerate to not exceed usb bandwidth */ + if (clockdiv < 3 && gspca_dev->width >= 320) + clockdiv = 3; + else if (clockdiv < 2) + clockdiv = 2; + + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> + exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ + exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); + if (exposure > 511) + exposure = 511; + + /* exposure register value is reversed! */ + exposure = 511 - exposure; + + sensor_write1(gspca_dev, 0x02, clockdiv); + sensor_write1(gspca_dev, 0x0e, exposure & 0xff); + sensor_write1(gspca_dev, 0x0f, exposure >> 8); + } +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) + return; + + if (sd->sensor_type) { + sensor_write1(gspca_dev, 0x0e, sd->gain); + } else { + sensor_write1(gspca_dev, 0x10, sd->gain); + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; } /* Include pac common sof detection functions */ @@ -320,8 +938,9 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0111)}, - {USB_DEVICE(0x093a, 0x010f)}, + {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ + {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ + {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 95a97ab684c..96659433d24 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -35,25 +35,17 @@ MODULE_LICENSE("GPL"); #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 -#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */ - -/* An exposure value of 4 also works (3 does not) but then we need to lower - the compression balance setting when in 352x288 mode, otherwise the usb - bandwidth is not enough and packets get dropped resulting in corrupt - frames. The problem with this is that when the compression balance gets - lowered below 0x80, the pac207 starts using a different compression - algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix - and currently we do not know how to decompress these lines, so for now - we use a minimum exposure value of 5 */ -#define PAC207_EXPOSURE_MIN 5 +#define PAC207_BRIGHTNESS_DEFAULT 46 + +#define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 26 -#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 ?? */ -#define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */ +#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ +#define PAC207_EXPOSURE_KNEE 8 /* 4 = 30 fps, 11 = 8, 15 = 6 */ #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */ -#define PAC207_GAIN_KNEE 20 +#define PAC207_GAIN_KNEE 31 #define PAC207_AUTOGAIN_DEADZONE 30 @@ -166,16 +158,12 @@ static const struct v4l2_pix_format sif_mode[] = { }; static const __u8 pac207_sensor_init[][8] = { - {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, - {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, + {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84}, + {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, - {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02}, {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00}, }; - /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ -static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; - static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { @@ -274,7 +262,6 @@ static int sd_init(struct gspca_dev *gspca_dev) * Bit_1=LED, * Bit_2=Compression test mode enable */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ - pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ return 0; } @@ -289,15 +276,13 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); - pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8); - pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8); - pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4); + pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8); /* Compression Balance */ if (gspca_dev->width == 176) pac207_write_reg(gspca_dev, 0x4a, 0xff); else - pac207_write_reg(gspca_dev, 0x4a, 0x88); + pac207_write_reg(gspca_dev, 0x4a, 0x30); pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ pac207_write_reg(gspca_dev, 0x08, sd->brightness); @@ -346,7 +331,7 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE, + 100, PAC207_AUTOGAIN_DEADZONE, PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index e1e3a3a5048..052714484e8 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -1057,6 +1057,7 @@ static struct sd_desc sd_desc = { /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, @@ -1068,6 +1069,7 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, {} diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index fcfbbd329b4..cdad3db3336 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -94,6 +94,16 @@ struct sd { #endif }; +struct i2c_reg_u8 { + u8 reg; + u8 val; +}; + +struct i2c_reg_u16 { + u8 reg; + u16 val; +}; + static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val); @@ -403,7 +413,7 @@ static const struct v4l2_pix_format sxga_mode[] = { .priv = 3 | MODE_RAW | MODE_SXGA}, }; -static const int hsv_red_x[] = { +static const s16 hsv_red_x[] = { 41, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 81, 83, 85, 87, @@ -451,7 +461,7 @@ static const int hsv_red_x[] = { 24, 26, 28, 30, 33, 35, 37, 39, 41 }; -static const int hsv_red_y[] = { +static const s16 hsv_red_y[] = { 82, 80, 78, 76, 74, 73, 71, 69, 67, 65, 63, 61, 58, 56, 54, 52, 50, 48, 46, 44, 41, 39, 37, 35, @@ -499,7 +509,7 @@ static const int hsv_red_y[] = { 96, 94, 92, 91, 89, 87, 85, 84, 82 }; -static const int hsv_green_x[] = { +static const s16 hsv_green_x[] = { -124, -124, -125, -125, -125, -125, -125, -125, -125, -126, -126, -125, -125, -125, -125, -125, -125, -124, -124, -124, -123, -123, -122, -122, @@ -547,7 +557,7 @@ static const int hsv_green_x[] = { -120, -120, -121, -122, -122, -123, -123, -124, -124 }; -static const int hsv_green_y[] = { +static const s16 hsv_green_y[] = { -100, -99, -98, -97, -95, -94, -93, -91, -90, -89, -87, -86, -84, -83, -81, -80, -78, -76, -75, -73, -71, -70, -68, -66, @@ -595,7 +605,7 @@ static const int hsv_green_y[] = { -109, -108, -107, -106, -105, -104, -103, -102, -100 }; -static const int hsv_blue_x[] = { +static const s16 hsv_blue_x[] = { 112, 113, 114, 114, 115, 116, 117, 117, 118, 118, 119, 119, 120, 120, 120, 121, 121, 121, 122, 122, 122, 122, 122, 122, @@ -643,7 +653,7 @@ static const int hsv_blue_x[] = { 104, 105, 106, 107, 108, 109, 110, 111, 112 }; -static const int hsv_blue_y[] = { +static const s16 hsv_blue_y[] = { -11, -13, -15, -17, -19, -21, -23, -25, -27, -29, -31, -33, -35, -37, -39, -41, -43, -45, -46, -48, -50, -52, -54, -55, @@ -792,21 +802,21 @@ static u8 hv7131r_gain[] = { 0x78 /* 8x */ }; -static u8 soi968_init[][2] = { +static struct i2c_reg_u8 soi968_init[] = { {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, - {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13}, + {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13}, {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, }; -static u8 ov7660_init[][2] = { +static struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, @@ -815,7 +825,7 @@ static u8 ov7660_init[][2] = { {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, }; -static u8 ov7670_init[][2] = { +static struct i2c_reg_u8 ov7670_init[] = { {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, @@ -872,7 +882,7 @@ static u8 ov7670_init[][2] = { {0x93, 0x00}, }; -static u8 ov9650_init[][2] = { +static struct i2c_reg_u8 ov9650_init[] = { {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, @@ -902,7 +912,7 @@ static u8 ov9650_init[][2] = { {0xaa, 0x92}, {0xab, 0x0a}, }; -static u8 ov9655_init[][2] = { +static struct i2c_reg_u8 ov9655_init[] = { {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, @@ -939,7 +949,7 @@ static u8 ov9655_init[][2] = { {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, }; -static u16 mt9v112_init[][2] = { +static struct i2c_reg_u16 mt9v112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, @@ -958,7 +968,7 @@ static u16 mt9v112_init[][2] = { {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, }; -static u16 mt9v111_init[][2] = { +static struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, @@ -985,7 +995,7 @@ static u16 mt9v111_init[][2] = { {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, }; -static u16 mt9v011_init[][2] = { +static struct i2c_reg_u16 mt9v011_init[] = { {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, @@ -1012,7 +1022,7 @@ static u16 mt9v011_init[][2] = { {0x06, 0x0029}, {0x05, 0x0009}, }; -static u16 mt9m001_init[][2] = { +static struct i2c_reg_u16 mt9m001_init[] = { {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e}, {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501}, {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, @@ -1025,14 +1035,14 @@ static u16 mt9m001_init[][2] = { {0x2e, 0x0029}, {0x07, 0x0002}, }; -static u16 mt9m111_init[][2] = { - {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009}, - {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, - {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200}, - {0x06, 0x308e}, {0xf0, 0x0000}, +static struct i2c_reg_u16 mt9m111_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, }; -static u8 hv7131r_init[][2] = { +static struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, @@ -1043,7 +1053,7 @@ static u8 hv7131r_init[][2] = { {0x23, 0x09}, {0x01, 0x08}, }; -int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) { struct usb_device *dev = gspca_dev->dev; int result; @@ -1062,7 +1072,8 @@ int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) return 0; } -int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) +static int reg_w(struct gspca_dev *gspca_dev, u16 reg, + const u8 *buffer, int length) { struct usb_device *dev = gspca_dev->dev; int result; @@ -1082,13 +1093,13 @@ int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) return 0; } -int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) { u8 data[1] = {value}; return reg_w(gspca_dev, reg, data, 1); } -int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) { int i; reg_w(gspca_dev, 0x10c0, buffer, 8); @@ -1096,15 +1107,15 @@ int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) reg_r(gspca_dev, 0x10c0, 1); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) - return -1; + return -EIO; return 0; } msleep(1); } - return -1; + return -EIO; } -int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1126,7 +1137,7 @@ int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) return i2c_w(gspca_dev, row); } -int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1152,7 +1163,7 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1160,14 +1171,15 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (2 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (1 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = gspca_dev->usb_buf[4]; return 0; } @@ -1176,7 +1188,7 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1184,14 +1196,15 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (3 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (2 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; return 0; } @@ -1201,8 +1214,8 @@ static int ov9650_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { - if (i2c_w1(gspca_dev, ov9650_init[i][0], - ov9650_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov9650_init[i].reg, + ov9650_init[i].val) < 0) { err("OV9650 sensor initialization failed"); return -ENODEV; } @@ -1218,8 +1231,8 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { - if (i2c_w1(gspca_dev, ov9655_init[i][0], - ov9655_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov9655_init[i].reg, + ov9655_init[i].val) < 0) { err("OV9655 sensor initialization failed"); return -ENODEV; } @@ -1237,14 +1250,14 @@ static int soi968_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { - if (i2c_w1(gspca_dev, soi968_init[i][0], - soi968_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, soi968_init[i].reg, + soi968_init[i].val) < 0) { err("SOI968 sensor initialization failed"); return -ENODEV; } } /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << EXPOSURE_IDX); sd->hstart = 60; sd->vstart = 11; return 0; @@ -1256,8 +1269,8 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { - if (i2c_w1(gspca_dev, ov7660_init[i][0], - ov7660_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov7660_init[i].reg, + ov7660_init[i].val) < 0) { err("OV7660 sensor initialization failed"); return -ENODEV; } @@ -1275,8 +1288,8 @@ static int ov7670_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { - if (i2c_w1(gspca_dev, ov7670_init[i][0], - ov7670_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov7670_init[i].reg, + ov7670_init[i].val) < 0) { err("OV7670 sensor initialization failed"); return -ENODEV; } @@ -1299,8 +1312,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0xff, &value); if ((ret == 0) && (value == 0x8243)) { for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { - if (i2c_w2(gspca_dev, mt9v011_init[i][0], - mt9v011_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v011_init[i].reg, + mt9v011_init[i].val) < 0) { err("MT9V011 sensor initialization failed"); return -ENODEV; } @@ -1317,8 +1330,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0xff, &value); if ((ret == 0) && (value == 0x823a)) { for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { - if (i2c_w2(gspca_dev, mt9v111_init[i][0], - mt9v111_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v111_init[i].reg, + mt9v111_init[i].val) < 0) { err("MT9V111 sensor initialization failed"); return -ENODEV; } @@ -1339,8 +1352,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0x00, &value); if ((ret == 0) && (value == 0x1229)) { for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { - if (i2c_w2(gspca_dev, mt9v112_init[i][0], - mt9v112_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v112_init[i].reg, + mt9v112_init[i].val) < 0) { err("MT9V112 sensor initialization failed"); return -ENODEV; } @@ -1360,12 +1373,13 @@ static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { - if (i2c_w2(gspca_dev, mt9m111_init[i][0], - mt9m111_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9m111_init[i].reg, + mt9m111_init[i].val) < 0) { err("MT9M111 sensor initialization failed"); return -ENODEV; } } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); sd->hstart = 0; sd->vstart = 2; return 0; @@ -1376,8 +1390,8 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { - if (i2c_w2(gspca_dev, mt9m001_init[i][0], - mt9m001_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9m001_init[i].reg, + mt9m001_init[i].val) < 0) { err("MT9M001 sensor initialization failed"); return -ENODEV; } @@ -1395,8 +1409,8 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { - if (i2c_w1(gspca_dev, hv7131r_init[i][0], - hv7131r_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, hv7131r_init[i].reg, + hv7131r_init[i].val) < 0) { err("HV7131R Sensor initialization failed"); return -ENODEV; } @@ -1620,7 +1634,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: - case SENSOR_SOI968: case SENSOR_OV9655: case SENSOR_OV9650: exp[0] |= (3 << 4); @@ -1629,7 +1642,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) exp[4] = sd->exposure >> 8; break; case SENSOR_MT9M001: - case SENSOR_MT9M111: case SENSOR_MT9V112: case SENSOR_MT9V111: case SENSOR_MT9V011: @@ -1645,6 +1657,8 @@ static int set_exposure(struct gspca_dev *gspca_dev) exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; break; + default: + return 0; } i2c_w(gspca_dev, exp); return 0; @@ -1671,7 +1685,6 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[4] = micron1_gain[sd->gain] & 0xff; break; case SENSOR_MT9V112: - case SENSOR_MT9M111: gain[0] |= (3 << 4); gain[2] = 0x2f; gain[3] = micron1_gain[sd->gain] >> 8; @@ -1688,6 +1701,8 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[2] = 0x30; gain[3] = hv7131r_gain[sd->gain]; break; + default: + return 0; } i2c_w(gspca_dev, gain); return 0; @@ -1990,7 +2005,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->i2c_addr = id->driver_info & 0xff; switch (sd->sensor) { + case SENSOR_MT9M111: case SENSOR_OV9650: + case SENSOR_SOI968: cam->cam_mode = sxga_mode; cam->nmodes = ARRAY_SIZE(sxga_mode); break; @@ -2106,6 +2123,25 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) struct sd *sd = (struct sd *) gspca_dev; u8 value; switch (sd->sensor) { + case SENSOR_SOI968: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1d); + i2c_w1(gspca_dev, 0x18, 0xbd); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x81); + i2c_w1(gspca_dev, 0x12, 0x00); + sd->hstart = 140; + sd->vstart = 19; + } else { + i2c_w1(gspca_dev, 0x17, 0x13); + i2c_w1(gspca_dev, 0x18, 0x63); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x79); + i2c_w1(gspca_dev, 0x12, 0x40); + sd->hstart = 60; + sd->vstart = 11; + } + break; case SENSOR_OV9650: if (mode & MODE_SXGA) { i2c_w1(gspca_dev, 0x17, 0x1b); @@ -2123,6 +2159,17 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); } break; + case SENSOR_MT9M111: + if (mode & MODE_SXGA) { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x970b); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } else { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x8000); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + break; } } @@ -2211,15 +2258,10 @@ static void sd_stop0(struct gspca_dev *gspca_dev) kfree(sd->jpeg_hdr); } -static void do_autoexposure(struct gspca_dev *gspca_dev) +static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; - int avg_lum, new_exp; - - if (!sd->auto_exposure) - return; - - avg_lum = atomic_read(&sd->avg_lum); + s16 new_exp; /* * some hardcoded values are present @@ -2266,6 +2308,39 @@ static void do_autoexposure(struct gspca_dev *gspca_dev) } } +static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (avg_lum < MIN_AVG_LUM) { + if (sd->gain + 1 <= 28) { + sd->gain++; + set_gain(gspca_dev); + } + } + if (avg_lum > MAX_AVG_LUM) { + if (sd->gain - 1 >= 0) { + sd->gain--; + set_gain(gspca_dev); + } + } +} + +static void sd_dqcallback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + + if (!sd->auto_exposure) + return; + + avg_lum = atomic_read(&sd->avg_lum); + if (sd->sensor == SENSOR_SOI968) + do_autogain(gspca_dev, avg_lum); + else + do_autoexposure(gspca_dev, avg_lum); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ @@ -2333,7 +2408,7 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, - .dq_callback = do_autoexposure, + .dq_callback = sd_dqcallback, #ifdef CONFIG_VIDEO_ADV_DEBUG .set_register = sd_dbg_s_register, .get_register = sd_dbg_g_register, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index d6332ab8066..33f4d0a1f6f 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -727,7 +727,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ - {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, /* GAIN BLUE RED VREF */ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, /* COM 1 BAVE GEAVE AECHH */ @@ -783,7 +783,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ - {0xb1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, /****** (some exchanges in the win trace) ******/ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ /* bits[3..0]reserved */ @@ -1145,17 +1145,12 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x42); break; case SENSOR_OV7660: - reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); - reg_w1(gspca_dev, 0x01, 0x60); - reg_w1(gspca_dev, 0x01, 0x40); - break; case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); - mdelay(100); + msleep(100); reg_w1(gspca_dev, 0x02, 0x62); break; /* case SENSOR_HV7131R: */ @@ -1624,6 +1619,8 @@ static void setvflip(struct sd *sd) static void setinfrared(struct sd *sd) { + if (sd->gspca_dev.ctrl_dis & (1 << INFRARED_IDX)) + return; /*fixme: different sequence for StarCam Clip and StarCam 370i */ /* Clip */ i2c_w1(&sd->gspca_dev, 0x02, /* gpio */ @@ -1637,16 +1634,19 @@ static void setfreq(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) return; if (sd->sensor == SENSOR_OV7660) { + u8 com8; + + com8 = 0xdf; /* auto gain/wb/expo */ switch (sd->freq) { case 0: /* Banding filter disabled */ - i2c_w1(gspca_dev, 0x13, 0xdf); + i2c_w1(gspca_dev, 0x13, com8 | 0x20); break; case 1: /* 50 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x0a); break; case 2: /* 60 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x02); break; } @@ -1796,12 +1796,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x99, 0x60); break; case SENSOR_OV7660: - reg_w1(gspca_dev, 0x9a, 0x05); - if (sd->bridge == BRIDGE_SN9C105) - reg_w1(gspca_dev, 0x99, 0xff); - else - reg_w1(gspca_dev, 0x99, 0x5b); - break; case SENSOR_SP80708: reg_w1(gspca_dev, 0x9a, 0x05); reg_w1(gspca_dev, 0x99, 0x59); @@ -2325,18 +2319,19 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)}, /* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */ {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)}, -/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */ {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)}, /* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ @@ -2352,6 +2347,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, #endif +/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE @@ -2359,7 +2355,9 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, +/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index d48b27c648c..b74a34218da 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -1923,7 +1923,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->subtype = id->driver_info; sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 3a0c893f942..a199298a641 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -286,7 +286,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 2ed2669bac3..9696c4caf5c 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1304,19 +1304,70 @@ static int reg_read(struct gspca_dev *gspca_dev, return gspca_dev->usb_buf[0]; } +/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */ +static int ssi_w(struct gspca_dev *gspca_dev, + u16 reg, u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, retry; + + ret = reg_write(dev, 0x8802, reg >> 8); + if (ret < 0) + goto out; + ret = reg_write(dev, 0x8801, reg & 0x00ff); + if (ret < 0) + goto out; + if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */ + ret = reg_write(dev, 0x8805, val & 0x00ff); + if (ret < 0) + goto out; + val >>= 8; + } + ret = reg_write(dev, 0x8800, val); + if (ret < 0) + goto out; + + /* poll until not busy */ + retry = 10; + for (;;) { + ret = reg_read(gspca_dev, 0x8803); + if (ret < 0) + break; + if (gspca_dev->usb_buf[0] == 0) + break; + if (--retry <= 0) { + PDEBUG(D_ERR, "ssi_w busy %02x", + gspca_dev->usb_buf[0]); + ret = -1; + break; + } + msleep(8); + } + +out: + return ret; +} + static int write_vector(struct gspca_dev *gspca_dev, const u16 (*data)[2]) { struct usb_device *dev = gspca_dev->dev; - int ret; + int ret = 0; while ((*data)[1] != 0) { - ret = reg_write(dev, (*data)[1], (*data)[0]); + if ((*data)[1] & 0x8000) { + if ((*data)[1] == 0xdd00) /* delay */ + msleep((*data)[0]); + else + ret = reg_write(dev, (*data)[1], (*data)[0]); + } else { + ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]); + } if (ret < 0) - return ret; + break; data++; } - return 0; + return ret; } /* this function is called at probe time */ diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 0da8e0de045..65489d6b0d8 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -50,7 +50,6 @@ int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) 0x04, 0x40, address, 0, buf, len, STV06XX_URB_MSG_TIMEOUT); - PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d", i2c_data, address, err); @@ -69,7 +68,7 @@ int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) *i2c_data = buf[0]; - PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d", + PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d", *i2c_data, address, err); return (err < 0) ? err : 0; @@ -111,14 +110,14 @@ int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; - PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); + PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); for (i = 0; i < len;) { /* Build the command buffer */ memset(buf, 0, I2C_BUFFER_LENGTH); for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) { buf[j] = data[2*i]; buf[0x10 + j] = data[2*i+1]; - PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x", + PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x", data[2*i+1], data[2*i]); } buf[0x20] = sd->sensor->i2c_addr; @@ -128,10 +127,10 @@ int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) 0x04, 0x40, 0x0400, 0, buf, I2C_BUFFER_LENGTH, STV06XX_URB_MSG_TIMEOUT); - if (err < 0) - return err; - } - return stv06xx_write_sensor_finish(sd); + if (err < 0) + return err; + } + return stv06xx_write_sensor_finish(sd); } int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) @@ -140,7 +139,7 @@ int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; - PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); + PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); for (i = 0; i < len;) { /* Build the command buffer */ @@ -149,7 +148,7 @@ int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) buf[j] = data[2*i]; buf[0x10 + j * 2] = data[2*i+1]; buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8; - PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x", + PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x", data[2*i+1], data[2*i]); } buf[0x20] = sd->sensor->i2c_addr; @@ -189,7 +188,7 @@ int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, STV06XX_URB_MSG_TIMEOUT); if (err < 0) { - PDEBUG(D_ERR, "I2C Read: error writing address: %d", err); + PDEBUG(D_ERR, "I2C: Read error writing address: %d", err); return err; } @@ -201,7 +200,7 @@ int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) else *value = buf[0]; - PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d", + PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d", *value, address, err); return (err < 0) ? err : 0; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index e5024c8496e..706e08dc525 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -37,7 +37,7 @@ static const struct ctrl hdcs1x00_ctrl[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "exposure", .minimum = 0x00, - .maximum = 0xffff, + .maximum = 0xff, .step = 0x1, .default_value = HDCS_DEFAULT_EXPOSURE, .flags = V4L2_CTRL_FLAG_SLIDER @@ -74,7 +74,35 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { } }; -static const struct ctrl hdcs1020_ctrl[] = {}; +static const struct ctrl hdcs1020_ctrl[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xffff, + .step = 0x1, + .default_value = HDCS_DEFAULT_EXPOSURE, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_exposure, + .get = hdcs_get_exposure + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = HDCS_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_gain, + .get = hdcs_get_gain + } +}; static struct v4l2_pix_format hdcs1020_mode[] = { { @@ -120,6 +148,7 @@ struct hdcs { } exp; int psmp; + u8 exp_cache, gain_cache; }; static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) @@ -205,34 +234,8 @@ static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) struct sd *sd = (struct sd *) gspca_dev; struct hdcs *hdcs = sd->sensor_priv; - /* Column time period */ - int ct; - /* Column processing period */ - int cp; - /* Row processing period */ - int rp; - int cycles; - int err; - int rowexp; - u16 data[2]; - - err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]); - if (err < 0) - return err; - - err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]); - if (err < 0) - return err; - - rowexp = (data[1] << 8) | data[0]; - - ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); - cp = hdcs->exp.cto + (hdcs->w * ct / 2); - rp = hdcs->exp.rs + cp; + *val = hdcs->exp_cache; - cycles = rp * rowexp; - *val = cycles / HDCS_CLK_FREQ_MHZ; - PDEBUG(D_V4L2, "Read exposure %d", *val); return 0; } @@ -252,9 +255,12 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) within the column processing period */ int mnct; int cycles, err; - u8 exp[4]; + u8 exp[14]; - cycles = val * HDCS_CLK_FREQ_MHZ; + val &= 0xff; + hdcs->exp_cache = val; + + cycles = val * HDCS_CLK_FREQ_MHZ * 257; ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); cp = hdcs->exp.cto + (hdcs->w * ct / 2); @@ -288,73 +294,79 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) srowexp = max_srowexp; if (IS_1020(sd)) { - exp[0] = rowexp & 0xff; - exp[1] = rowexp >> 8; - exp[2] = (srowexp >> 2) & 0xff; - /* this clears exposure error flag */ - exp[3] = 0x1; - err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); + exp[0] = HDCS20_CONTROL; + exp[1] = 0x00; /* Stop streaming */ + exp[2] = HDCS_ROWEXPL; + exp[3] = rowexp & 0xff; + exp[4] = HDCS_ROWEXPH; + exp[5] = rowexp >> 8; + exp[6] = HDCS20_SROWEXP; + exp[7] = (srowexp >> 2) & 0xff; + exp[8] = HDCS20_ERROR; + exp[9] = 0x10; /* Clear exposure error flag*/ + exp[10] = HDCS20_CONTROL; + exp[11] = 0x04; /* Restart streaming */ + err = stv06xx_write_sensor_bytes(sd, exp, 6); } else { - exp[0] = rowexp & 0xff; - exp[1] = rowexp >> 8; - exp[2] = srowexp & 0xff; - exp[3] = srowexp >> 8; - err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); + exp[0] = HDCS00_CONTROL; + exp[1] = 0x00; /* Stop streaming */ + exp[2] = HDCS_ROWEXPL; + exp[3] = rowexp & 0xff; + exp[4] = HDCS_ROWEXPH; + exp[5] = rowexp >> 8; + exp[6] = HDCS00_SROWEXPL; + exp[7] = srowexp & 0xff; + exp[8] = HDCS00_SROWEXPH; + exp[9] = srowexp >> 8; + exp[10] = HDCS_STATUS; + exp[11] = 0x10; /* Clear exposure error flag*/ + exp[12] = HDCS00_CONTROL; + exp[13] = 0x04; /* Restart streaming */ + err = stv06xx_write_sensor_bytes(sd, exp, 7); if (err < 0) return err; - - /* clear exposure error flag */ - err = stv06xx_write_sensor(sd, - HDCS_STATUS, BIT(4)); } PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", val, rowexp, srowexp); return err; } -static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b) +static int hdcs_set_gains(struct sd *sd, u8 g) { + struct hdcs *hdcs = sd->sensor_priv; + int err; u8 gains[4]; + hdcs->gain_cache = g; + /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ - if (r > 127) - r = 0x80 | (r / 2); if (g > 127) g = 0x80 | (g / 2); - if (b > 127) - b = 0x80 | (b / 2); gains[0] = g; - gains[1] = r; - gains[2] = b; + gains[1] = g; + gains[2] = g; gains[3] = g; - return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); + err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); + return err; } static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - int err; - u16 data; - - err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data); + struct hdcs *hdcs = sd->sensor_priv; - /* Bit 7 doubles the gain */ - if (data & 0x80) - *val = (data & 0x7f) * 2; - else - *val = data; + *val = hdcs->gain_cache; - PDEBUG(D_V4L2, "Read gain %d", *val); - return err; + return 0; } static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) { PDEBUG(D_V4L2, "Writing gain %d", val); return hdcs_set_gains((struct sd *) gspca_dev, - val & 0xff, val & 0xff, val & 0xff); + val & 0xff); } static int hdcs_set_size(struct sd *sd, @@ -572,16 +584,15 @@ static int hdcs_init(struct sd *sd) if (err < 0) return err; - err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN, - HDCS_DEFAULT_GAIN); + err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN); if (err < 0) return err; - err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); + err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); if (err < 0) return err; - err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); + err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); return err; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h index 412f06cf3d5..37b31c99d95 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -124,7 +124,7 @@ #define HDCS_RUN_ENABLE (1 << 2) #define HDCS_SLEEP_MODE (1 << 1) -#define HDCS_DEFAULT_EXPOSURE 5000 +#define HDCS_DEFAULT_EXPOSURE 48 #define HDCS_DEFAULT_GAIN 128 static int hdcs_probe_1x00(struct sd *sd); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index 87cb5b9ddfa..c11f06e4ae7 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -166,7 +166,7 @@ static int st6422_init(struct sd *sd) /* 10 compressed? */ { 0x1439, 0x00 }, -/* antiflimmer?? 0xa2 ger perfekt bild mot monitor */ +/* anti-noise? 0xa2 gives a perfect image */ { 0x143b, 0x05 }, { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ @@ -197,15 +197,14 @@ static int st6422_init(struct sd *sd) { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ { 0x1501, 0xaf }, -/* high val-> ljus area blir morkare. */ -/* low val -> ljus area blir ljusare. */ +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ { 0x1502, 0xc2 }, -/* high val-> ljus area blir morkare. */ -/* low val -> ljus area blir ljusare. */ +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ { 0x1503, 0x45 }, -/* high val-> ljus area blir morkare. */ -/* low val -> ljus area blir ljusare. */ - +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ { 0x1505, 0x02 }, /* 2 : 324x248 80352 bytes */ /* 7 : 248x162 40176 bytes */ diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 5127bbf9dd2..aa8f995ce04 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -32,26 +32,27 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char colors; - unsigned char autogain; + s8 brightness; + u8 contrast; + u8 colors; + u8 autogain; u8 quality; #define QUALITY_MIN 70 #define QUALITY_MAX 95 #define QUALITY_DEF 85 - char bridge; + u8 bridge; #define BRIDGE_SPCA504 0 #define BRIDGE_SPCA504B 1 #define BRIDGE_SPCA504C 2 #define BRIDGE_SPCA533 3 #define BRIDGE_SPCA536 4 - char subtype; + u8 subtype; #define AiptekMiniPenCam13 1 #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 +#define MegaImageVI 5 u8 *jpeg_hdr; }; @@ -67,21 +68,20 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", - .minimum = 0, - .maximum = 0xff, + .minimum = -128, + .maximum = 127, .step = 1, - .default_value = 0, +#define BRIGHTNESS_DEF 0 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, }, -#define SD_CONTRAST 1 { { .id = V4L2_CID_CONTRAST, @@ -90,12 +90,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0x20, +#define CONTRAST_DEF 0x20 + .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_COLOR 2 { { .id = V4L2_CID_SATURATION, @@ -104,12 +104,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0x1a, +#define COLOR_DEF 0x1a + .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, -#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -118,7 +118,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, }, .set = sd_setautogain, .get = sd_getautogain, @@ -180,14 +181,20 @@ static const struct v4l2_pix_format vga_mode2[] = { #define SPCA504_PCCAM600_OFFSET_MODE 5 #define SPCA504_PCCAM600_OFFSET_DATA 14 /* Frame packet header offsets for the spca533 */ -#define SPCA533_OFFSET_DATA 16 +#define SPCA533_OFFSET_DATA 16 #define SPCA533_OFFSET_FRAMSEQ 15 /* Frame packet header offsets for the spca536 */ -#define SPCA536_OFFSET_DATA 4 -#define SPCA536_OFFSET_FRAMSEQ 1 +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +struct cmd { + u8 req; + u16 val; + u16 idx; +}; /* Initialisation data for the Creative PC-CAM 600 */ -static const __u16 spca504_pccam600_init_data[][3] = { +static const struct cmd spca504_pccam600_init_data[] = { /* {0xa0, 0x0000, 0x0503}, * capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, @@ -211,22 +218,20 @@ static const __u16 spca504_pccam600_init_data[][3] = { {0x00, 0x0003, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, - {} }; /* Creative PC-CAM 600 specific open data, sent before using the * generic initialisation data from spca504_open_data. */ -static const __u16 spca504_pccam600_open_data[][3] = { +static const struct cmd spca504_pccam600_open_data[] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0500, 0x0001}, /* snapshot mode */ {0x00, 0x0003, 0x2880}, {0x00, 0x0001, 0x2881}, - {} }; /* Initialisation data for the logitech clicksmart 420 */ -static const __u16 spca504A_clicksmart420_init_data[][3] = { +static const struct cmd spca504A_clicksmart420_init_data[] = { /* {0xa0, 0x0000, 0x0503}, * capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, @@ -243,7 +248,7 @@ static const __u16 spca504A_clicksmart420_init_data[][3] = { {0xb0, 0x0001, 0x0000}, - {0x0a1, 0x0080, 0x0001}, + {0xa1, 0x0080, 0x0001}, {0x30, 0x0049, 0x0000}, {0x30, 0x0060, 0x0005}, {0x0c, 0x0004, 0x0000}, @@ -253,11 +258,10 @@ static const __u16 spca504A_clicksmart420_init_data[][3] = { {0x00, 0x0003, 0x2000}, {0x00, 0x0000, 0x2000}, - {} }; /* clicksmart 420 open data ? */ -static const __u16 spca504A_clicksmart420_open_data[][3] = { +static const struct cmd spca504A_clicksmart420_open_data[] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0502, 0x0000}, {0x06, 0x0000, 0x0000}, @@ -401,10 +405,9 @@ static const __u16 spca504A_clicksmart420_open_data[][3] = { {0x00, 0x0028, 0x287f}, {0xa0, 0x0000, 0x0503}, - {} }; -static const __u8 qtable_creative_pccam[2][64] = { +static const u8 qtable_creative_pccam[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, @@ -429,7 +432,7 @@ static const __u8 qtable_creative_pccam[2][64] = { * except for one byte. Possibly a typo? * NWG: 18/05/2003. */ -static const __u8 qtable_spca504_default[2][64] = { +static const u8 qtable_spca504_default[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, @@ -453,9 +456,9 @@ static const __u8 qtable_spca504_default[2][64] = { /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 len) + u8 req, + u16 index, + u16 len) { #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { @@ -473,31 +476,26 @@ static void reg_r(struct gspca_dev *gspca_dev, 500); } -/* write <len> bytes from gspca_dev->usb_buf */ -static void reg_w(struct gspca_dev *gspca_dev, - __u16 req, - __u16 value, - __u16 index, - __u16 len) +/* write one byte */ +static void reg_w_1(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u16 byte) { -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - err("reg_w: buffer overflow"); - return; - } -#endif + gspca_dev->usb_buf[0] = byte; usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, - len ? gspca_dev->usb_buf : NULL, len, + gspca_dev->usb_buf, 1, 500); } /* write req / index / value */ static int reg_w_riv(struct usb_device *dev, - __u16 req, __u16 index, __u16 value) + u8 req, u16 index, u16 value) { int ret; @@ -515,7 +513,7 @@ static int reg_w_riv(struct usb_device *dev, /* read 1 byte */ static int reg_r_1(struct gspca_dev *gspca_dev, - __u16 value) /* wValue */ + u16 value) /* wValue */ { int ret; @@ -536,9 +534,9 @@ static int reg_r_1(struct gspca_dev *gspca_dev, /* read 1 or 2 bytes - returns < 0 if error */ static int reg_r_12(struct gspca_dev *gspca_dev, - __u16 req, /* bRequest */ - __u16 index, /* wIndex */ - __u16 length) /* wLength (1 or 2 only) */ + u8 req, /* bRequest */ + u16 index, /* wIndex */ + u16 length) /* wLength (1 or 2 only) */ { int ret; @@ -559,43 +557,40 @@ static int reg_r_12(struct gspca_dev *gspca_dev, } static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) + const struct cmd *data, int ncmds) { struct usb_device *dev = gspca_dev->dev; - int ret, i = 0; + int ret; - while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { - ret = reg_w_riv(dev, data[i][0], data[i][2], data[i][1]); + while (--ncmds >= 0) { + ret = reg_w_riv(dev, data->req, data->idx, data->val); if (ret < 0) { PDEBUG(D_ERR, - "Register write failed for 0x%x,0x%x,0x%x", - data[i][0], data[i][1], data[i][2]); + "Register write failed for 0x%02x, 0x%04x, 0x%04x", + data->req, data->val, data->idx); return ret; } - i++; + data++; } return 0; } static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, - unsigned int request, - unsigned int ybase, - unsigned int cbase, - const __u8 qtable[2][64]) + const u8 qtable[2][64]) { struct usb_device *dev = gspca_dev->dev; int i, err; /* loop over y components */ for (i = 0; i < 64; i++) { - err = reg_w_riv(dev, request, ybase + i, qtable[0][i]); + err = reg_w_riv(dev, 0x00, 0x2800 + i, qtable[0][i]); if (err < 0) return err; } /* loop over c components */ for (i = 0; i < 64; i++) { - err = reg_w_riv(dev, request, cbase + i, qtable[1][i]); + err = reg_w_riv(dev, 0x00, 0x2840 + i, qtable[1][i]); if (err < 0) return err; } @@ -603,34 +598,34 @@ static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, } static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, - __u16 req, __u16 idx, __u16 val) + u8 req, u16 idx, u16 val) { struct usb_device *dev = gspca_dev->dev; - __u8 notdone; + int notdone; reg_w_riv(dev, req, idx, val); notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); reg_w_riv(dev, req, idx, val); - PDEBUG(D_FRAM, "before wait 0x%x", notdone); + PDEBUG(D_FRAM, "before wait 0x%04x", notdone); msleep(200); notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); - PDEBUG(D_FRAM, "after wait 0x%x", notdone); + PDEBUG(D_FRAM, "after wait 0x%04x", notdone); } static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, - __u16 req, - __u16 idx, __u16 val, __u8 stat, __u8 count) + u8 req, + u16 idx, u16 val, u8 stat, u8 count) { struct usb_device *dev = gspca_dev->dev; - __u8 status; - __u8 endcode; + int status; + u8 endcode; reg_w_riv(dev, req, idx, val); status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); endcode = stat; - PDEBUG(D_FRAM, "Status 0x%x Need 0x%x", status, stat); + PDEBUG(D_FRAM, "Status 0x%x Need 0x%04x", status, stat); if (!count) return; count = 200; @@ -640,7 +635,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, /* reg_w_riv(dev, req, idx, val); */ status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); if (status == endcode) { - PDEBUG(D_FRAM, "status 0x%x after wait 0x%x", + PDEBUG(D_FRAM, "status 0x%04x after wait %d", status, 200 - count); break; } @@ -667,8 +662,7 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) while (--count > 0) { reg_r(gspca_dev, 0x21, 1, 1); if (gspca_dev->usb_buf[0] != 0) { - gspca_dev->usb_buf[0] = 0; - reg_w(gspca_dev, 0x21, 0, 1, 1); + reg_w_1(gspca_dev, 0x21, 0, 1, 0); reg_r(gspca_dev, 0x21, 1, 1); spca504B_PollingDataReady(gspca_dev); break; @@ -679,7 +673,7 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { - __u8 *data; + u8 *data; data = gspca_dev->usb_buf; reg_r(gspca_dev, 0x20, 0, 5); @@ -693,41 +687,34 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 Size; - __u8 Type; + u8 Size; int rc; - Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - Type = 0; + Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->bridge) { case BRIDGE_SPCA533: - reg_w(gspca_dev, 0x31, 0, 0, 0); + reg_w_riv(dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); - gspca_dev->usb_buf[0] = 2; /* type */ - reg_w(gspca_dev, 0x24, 0, 8, 1); + reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ reg_r(gspca_dev, 0x24, 8, 1); - gspca_dev->usb_buf[0] = Size; - reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_w_1(gspca_dev, 0x25, 0, 4, Size); reg_r(gspca_dev, 0x25, 4, 1); /* size */ rc = spca504B_PollingDataReady(gspca_dev); /* Init the cam width height with some values get on init ? */ - reg_w(gspca_dev, 0x31, 0, 4, 0); + reg_w_riv(dev, 0x31, 0, 0x04); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); break; default: /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA536: */ - gspca_dev->usb_buf[0] = Size; - reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_w_1(gspca_dev, 0x25, 0, 4, Size); reg_r(gspca_dev, 0x25, 4, 1); /* size */ - Type = 6; - gspca_dev->usb_buf[0] = Type; - reg_w(gspca_dev, 0x27, 0, 0, 1); + reg_w_1(gspca_dev, 0x27, 0, 0, 6); reg_r(gspca_dev, 0x27, 0, 1); /* type */ rc = spca504B_PollingDataReady(gspca_dev); break; @@ -767,17 +754,51 @@ static void spca504_wait_status(struct gspca_dev *gspca_dev) static void spca504B_setQtable(struct gspca_dev *gspca_dev) { - gspca_dev->usb_buf[0] = 3; - reg_w(gspca_dev, 0x26, 0, 0, 1); + reg_w_1(gspca_dev, 0x26, 0, 0, 3); reg_r(gspca_dev, 0x26, 0, 1); spca504B_PollingDataReady(gspca_dev); } -static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; + reg_w_riv(dev, 0x00, reg, sd->brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; + reg_w_riv(dev, 0x00, reg, sd->contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; + reg_w_riv(dev, 0x00, reg, sd->colors); +} + +static void init_ctl_reg(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; int pollreg = 1; + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); + switch (sd->bridge) { case BRIDGE_SPCA504: case BRIDGE_SPCA504C: @@ -786,20 +807,14 @@ static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) default: /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA504B: */ - reg_w(gspca_dev, 0, 0, 0x21a7, 0); /* brightness */ - reg_w(gspca_dev, 0, 0x20, 0x21a8, 0); /* contrast */ - reg_w(gspca_dev, 0, 0, 0x21ad, 0); /* hue */ - reg_w(gspca_dev, 0, 1, 0x21ac, 0); /* sat/hue */ - reg_w(gspca_dev, 0, 0x20, 0x21ae, 0); /* saturation */ - reg_w(gspca_dev, 0, 0, 0x21a3, 0); /* gamma */ + reg_w_riv(dev, 0, 0x00, 0x21ad); /* hue */ + reg_w_riv(dev, 0, 0x01, 0x21ac); /* sat/hue */ + reg_w_riv(dev, 0, 0x00, 0x21a3); /* gamma */ break; case BRIDGE_SPCA536: - reg_w(gspca_dev, 0, 0, 0x20f0, 0); - reg_w(gspca_dev, 0, 0x21, 0x20f1, 0); - reg_w(gspca_dev, 0, 0x40, 0x20f5, 0); - reg_w(gspca_dev, 0, 1, 0x20f4, 0); - reg_w(gspca_dev, 0, 0x40, 0x20f6, 0); - reg_w(gspca_dev, 0, 0, 0x2089, 0); + reg_w_riv(dev, 0, 0x40, 0x20f5); + reg_w_riv(dev, 0, 0x01, 0x20f4); + reg_w_riv(dev, 0, 0x00, 0x2089); break; } if (pollreg) @@ -840,20 +855,24 @@ static int sd_config(struct gspca_dev *gspca_dev, /* case BRIDGE_SPCA504: */ /* case BRIDGE_SPCA536: */ cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes =ARRAY_SIZE(vga_mode); break; case BRIDGE_SPCA533: cam->cam_mode = custom_mode; - cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + if (sd->subtype == MegaImageVI) /* 320x240 only */ + cam->nmodes = ARRAY_SIZE(custom_mode) - 1; + else + cam->nmodes = ARRAY_SIZE(custom_mode); break; case BRIDGE_SPCA504C: cam->cam_mode = vga_mode2; - cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; + cam->nmodes = ARRAY_SIZE(vga_mode2); break; } - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->autogain = AUTOGAIN_DEF; sd->quality = QUALITY_DEF; return 0; } @@ -863,32 +882,29 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - int rc; - __u8 i; - __u8 info[6]; - int err_code; + int i, err_code; + u8 info[6]; switch (sd->bridge) { case BRIDGE_SPCA504B: - reg_w(gspca_dev, 0x1d, 0, 0, 0); - reg_w(gspca_dev, 0, 1, 0x2306, 0); - reg_w(gspca_dev, 0, 0, 0x0d04, 0); - reg_w(gspca_dev, 0, 0, 0x2000, 0); - reg_w(gspca_dev, 0, 0x13, 0x2301, 0); - reg_w(gspca_dev, 0, 0, 0x2306, 0); + reg_w_riv(dev, 0x1d, 0x00, 0); + reg_w_riv(dev, 0, 0x01, 0x2306); + reg_w_riv(dev, 0, 0x00, 0x0d04); + reg_w_riv(dev, 0, 0x00, 0x2000); + reg_w_riv(dev, 0, 0x13, 0x2301); + reg_w_riv(dev, 0, 0x00, 0x2306); /* fall thru */ case BRIDGE_SPCA533: - rc = spca504B_PollingDataReady(gspca_dev); + spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); break; case BRIDGE_SPCA536: spca50x_GetFirmware(gspca_dev); reg_r(gspca_dev, 0x00, 0x5002, 1); - gspca_dev->usb_buf[0] = 0; - reg_w(gspca_dev, 0x24, 0, 0, 1); + reg_w_1(gspca_dev, 0x24, 0, 0, 0); reg_r(gspca_dev, 0x24, 0, 1); - rc = spca504B_PollingDataReady(gspca_dev); - reg_w(gspca_dev, 0x34, 0, 0, 0); + spca504B_PollingDataReady(gspca_dev); + reg_w_riv(dev, 0x34, 0, 0); spca504B_WaitCmdStatus(gspca_dev); break; case BRIDGE_SPCA504C: /* pccam600 */ @@ -898,12 +914,13 @@ static int sd_init(struct gspca_dev *gspca_dev) spca504_wait_status(gspca_dev); if (sd->subtype == LogitechClickSmart420) write_vector(gspca_dev, - spca504A_clicksmart420_open_data); + spca504A_clicksmart420_open_data, + ARRAY_SIZE(spca504A_clicksmart420_open_data)); else - write_vector(gspca_dev, spca504_pccam600_open_data); + write_vector(gspca_dev, spca504_pccam600_open_data, + ARRAY_SIZE(spca504_pccam600_open_data)); err_code = spca50x_setup_qtable(gspca_dev, - 0x00, 0x2800, - 0x2840, qtable_creative_pccam); + qtable_creative_pccam); if (err_code < 0) { PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed"); return err_code; @@ -941,8 +958,8 @@ static int sd_init(struct gspca_dev *gspca_dev) 6, 0, 0x86, 1); */ /* spca504A_acknowledged_command (gspca_dev, 0x24, 0, 0, 0x9D, 1); */ - reg_w_riv(dev, 0x0, 0x270c, 0x05); /* L92 sno1t.txt */ - reg_w_riv(dev, 0x0, 0x2310, 0x05); + reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */ + reg_w_riv(dev, 0x00, 0x2310, 0x05); spca504A_acknowledged_command(gspca_dev, 0x01, 0x0f, 0, 0xff, 0); } @@ -950,8 +967,6 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w_riv(dev, 0, 0x2000, 0); reg_w_riv(dev, 0, 0x2883, 1); err_code = spca50x_setup_qtable(gspca_dev, - 0x00, 0x2800, - 0x2840, qtable_spca504_default); if (err_code < 0) { PDEBUG(D_ERR, "spca50x_setup_qtable failed"); @@ -966,10 +981,9 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - int rc; int enable; - __u8 i; - __u8 info[6]; + int i; + u8 info[6]; /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); @@ -987,16 +1001,20 @@ static int sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ - if (sd->subtype == MegapixV4 || - sd->subtype == LogitechClickSmart820) { - reg_w(gspca_dev, 0xf0, 0, 0, 0); + switch (sd->subtype) { + case MegapixV4: + case LogitechClickSmart820: + case MegaImageVI: + reg_w_riv(dev, 0xf0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); reg_r(gspca_dev, 0xf0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); - } else { - reg_w(gspca_dev, 0x31, 0, 4, 0); + break; + default: + reg_w_riv(dev, 0x31, 0, 0x04); spca504B_WaitCmdStatus(gspca_dev); - rc = spca504B_PollingDataReady(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; } break; case BRIDGE_SPCA504: @@ -1030,15 +1048,17 @@ static int sd_start(struct gspca_dev *gspca_dev) spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); } spca504B_SetSizeType(gspca_dev); - reg_w_riv(dev, 0x0, 0x270c, 0x05); /* L92 sno1t.txt */ - reg_w_riv(dev, 0x0, 0x2310, 0x05); + reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */ + reg_w_riv(dev, 0x00, 0x2310, 0x05); break; case BRIDGE_SPCA504C: if (sd->subtype == LogitechClickSmart420) { write_vector(gspca_dev, - spca504A_clicksmart420_init_data); + spca504A_clicksmart420_init_data, + ARRAY_SIZE(spca504A_clicksmart420_init_data)); } else { - write_vector(gspca_dev, spca504_pccam600_init_data); + write_vector(gspca_dev, spca504_pccam600_init_data, + ARRAY_SIZE(spca504_pccam600_init_data)); } enable = (sd->autogain ? 0x04 : 0x01); reg_w_riv(dev, 0x0c, 0x0000, enable); /* auto exposure */ @@ -1050,7 +1070,7 @@ static int sd_start(struct gspca_dev *gspca_dev) spca504B_SetSizeType(gspca_dev); break; } - sp5xx_initContBrigHueRegisters(gspca_dev); + init_ctl_reg(gspca_dev); return 0; } @@ -1064,7 +1084,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ /* case BRIDGE_SPCA504B: */ - reg_w(gspca_dev, 0x31, 0, 0, 0); + reg_w_riv(dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); break; @@ -1082,7 +1102,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) 0x0f, 0x00, 0xff, 1); } else { spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); - reg_w_riv(dev, 0x01, 0x000f, 0x00); + reg_w_riv(dev, 0x01, 0x000f, 0x0000); } break; } @@ -1097,12 +1117,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; int i, sof = 0; - static unsigned char ffd9[] = {0xff, 0xd9}; + static u8 ffd9[] = {0xff, 0xd9}; /* frames are jpeg 4.1.1 without 0xff escape */ switch (sd->bridge) { @@ -1190,63 +1210,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21a7, sd->brightness); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f0, sd->brightness); - break; - } -} - -static void setcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21a8, sd->contrast); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f1, sd->contrast); - break; - } -} - -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21ae, sd->colors); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f6, sd->colors); - break; - } -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1384,6 +1347,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 404214b8cd2..1d321c30d22 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -264,6 +264,10 @@ static const struct v4l2_pix_format vga_mode_t16[] = { /* sensor specific data */ struct additional_sensor_data { + const u8 n3[6]; + const u8 *n4, n4sz; + const u8 reg80, reg8e; + const u8 nset8[6]; const u8 data1[10]; const u8 data2[9]; const u8 data3[9]; @@ -272,14 +276,55 @@ struct additional_sensor_data { const u8 stream[4]; }; +static const u8 n4_om6802[] = { + 0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46 +}; +static const u8 n4_other[] = { + 0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, + 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, + 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, + 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, + 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, + 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, + 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, + 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00 +}; +static const u8 n4_tas5130a[] = { + 0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20, + 0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4, + 0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10, + 0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08, + 0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a, + 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8, + 0xc6, 0xda +}; + static const struct additional_sensor_data sensor_data[] = { - { /* OM6802 */ + { /* 0: OM6802 */ + .n3 = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, + .n4 = n4_om6802, + .n4sz = sizeof n4_om6802, + .reg80 = 0x3c, + .reg8e = 0x33, + .nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00}, .data1 = {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06, 0xb3, 0xfc}, .data2 = {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, 0xff}, + .data3 = + {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, + 0xff}, .data4 = /*Freq (50/60Hz). Splitted for test purpose */ {0x66, 0xca, 0xa8, 0xf0}, .data5 = /* this could be removed later */ @@ -287,13 +332,23 @@ static const struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x78}, }, - { /* OTHER */ + { /* 1: OTHER */ + .n3 = + {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, + .n4 = n4_other, + .n4sz = sizeof n4_other, + .reg80 = 0xac, + .reg8e = 0xb8, + .nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00}, .data1 = {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a, 0xe8, 0xfc}, .data2 = {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, 0xd9}, + .data3 = + {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, + 0xd9}, .data4 = {0x66, 0x00, 0xa8, 0xa8}, .data5 = @@ -301,13 +356,23 @@ static const struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x00}, }, - { /* TAS5130A */ + { /* 2: TAS5130A */ + .n3 = + {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, + .n4 = n4_tas5130a, + .n4sz = sizeof n4_tas5130a, + .reg80 = 0x3c, + .reg8e = 0xb4, + .nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00}, .data1 = {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27, 0xc8, 0xfc}, .data2 = {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0}, + .data3 = + {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, + 0xe0}, .data4 = /* Freq (50/60Hz). Splitted for test purpose */ {0x66, 0x00, 0xa8, 0xe8}, .data5 = @@ -364,7 +429,7 @@ static const u8 gamma_table[GAMMA_MAX][17] = { {0x00, 0x18, 0x2b, 0x44, 0x60, 0x70, 0x80, 0x8e, /* 10 */ 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xd8, 0xe2, 0xf0, 0xff}, - {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8D, 0x9B, /* 11 */ + {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8d, 0x9b, /* 11 */ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, 0xff}, {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ @@ -385,8 +450,6 @@ static const u8 tas5130a_sensor_init[][8] = { {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, - {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, - {}, }; static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; @@ -633,10 +696,10 @@ static int sd_init(struct gspca_dev *gspca_dev) * but wont hurt anyway, and can help someone with similar webcam * to see the initial parameters.*/ struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; int i; u16 sensor_id; u8 test_byte = 0; - u16 reg80, reg8e; static const u8 read_indexs[] = { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, @@ -645,37 +708,6 @@ static int sd_init(struct gspca_dev *gspca_dev) {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; static const u8 n2[] = {0x08, 0x00}; - static const u8 n3[6] = - {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; - static const u8 n3_other[6] = - {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}; - static const u8 n4[] = - {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, - 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, - 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, - 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, - 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, - 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, - 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, - 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, - 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46}; - static const u8 n4_other[] = - {0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, - 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, - 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, - 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, - 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, - 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, - 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, - 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00}; - static const u8 nset8[6] = - { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 }; - static const u8 nset8_other[6] = - { 0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00 }; - static const u8 nset9[4] = - { 0x0b, 0x04, 0x0a, 0x78 }; - static const u8 nset9_other[4] = - { 0x0b, 0x04, 0x0a, 0x00 }; sensor_id = (reg_r(gspca_dev, 0x06) << 8) | reg_r(gspca_dev, 0x07); @@ -709,8 +741,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } if (i < 0) { err("Bad sensor reset %02x", test_byte); -/* return -EIO; */ -/*fixme: test - continue */ + return -EIO; } reg_w_buf(gspca_dev, n2, sizeof n2); } @@ -723,31 +754,17 @@ static int sd_init(struct gspca_dev *gspca_dev) i++; } - if (sd->sensor != SENSOR_OTHER) { - reg_w_buf(gspca_dev, n3, sizeof n3); - reg_w_buf(gspca_dev, n4, sizeof n4); - reg_r(gspca_dev, 0x0080); - reg_w(gspca_dev, 0x2c80); - reg80 = 0x3880; - reg8e = 0x338e; - } else { - reg_w_buf(gspca_dev, n3_other, sizeof n3_other); - reg_w_buf(gspca_dev, n4_other, sizeof n4_other); - sd->gamma = 5; - reg80 = 0xac80; - reg8e = 0xb88e; - } + sensor = &sensor_data[sd->sensor]; + reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3); + reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz); - reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, - sizeof sensor_data[sd->sensor].data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); - reg_w(gspca_dev, reg80); - reg_w(gspca_dev, reg80); - reg_w(gspca_dev, reg8e); + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -760,25 +777,14 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x2088); reg_w(gspca_dev, 0x2089); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4, - sizeof sensor_data[sd->sensor].data4); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data5, - sizeof sensor_data[sd->sensor].data5); - if (sd->sensor != SENSOR_OTHER) { - reg_w_buf(gspca_dev, nset8, sizeof nset8); - reg_w_buf(gspca_dev, nset9, sizeof nset9); - reg_w(gspca_dev, 0x2880); - } else { - reg_w_buf(gspca_dev, nset8_other, sizeof nset8_other); - reg_w_buf(gspca_dev, nset9_other, sizeof nset9_other); - } + reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); + reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); + reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); - reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, - sizeof sensor_data[sd->sensor].data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); return 0; } @@ -828,7 +834,6 @@ static void setlightfreq(struct gspca_dev *gspca_dev) * i added some module parameters for test with some users */ static void poll_sensor(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; static const u8 poll1[] = {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82, 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34, @@ -844,24 +849,23 @@ static void poll_sensor(struct gspca_dev *gspca_dev) 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, 0xc2, 0x80, 0xc3, 0x10}; - if (sd->sensor == SENSOR_OM6802) { - PDEBUG(D_STREAM, "[Sensor requires polling]"); - reg_w_buf(gspca_dev, poll1, sizeof poll1); - reg_w_buf(gspca_dev, poll2, sizeof poll2); - reg_w_buf(gspca_dev, poll3, sizeof poll3); - reg_w_buf(gspca_dev, poll4, sizeof poll4); - } + PDEBUG(D_STREAM, "[Sensor requires polling]"); + reg_w_buf(gspca_dev, poll1, sizeof poll1); + reg_w_buf(gspca_dev, poll2, sizeof poll2); + reg_w_buf(gspca_dev, poll3, sizeof poll3); + reg_w_buf(gspca_dev, poll4, sizeof poll4); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; int i, mode; u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; static const u8 t3[] = { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 }; - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (mode) { case 0: /* 640x480 (0x00) */ break; @@ -889,34 +893,33 @@ static int sd_start(struct gspca_dev *gspca_dev) default: /* case SENSOR_TAS5130A: */ i = 0; - while (tas5130a_sensor_init[i][0] != 0) { + for (;;) { reg_w_buf(gspca_dev, tas5130a_sensor_init[i], sizeof tas5130a_sensor_init[0]); + if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1) + break; i++; } reg_w(gspca_dev, 0x3c80); /* just in case and to keep sync with logs (for mine) */ - reg_w_buf(gspca_dev, tas5130a_sensor_init[3], + reg_w_buf(gspca_dev, tas5130a_sensor_init[i], sizeof tas5130a_sensor_init[0]); reg_w(gspca_dev, 0x3c80); break; } - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4, - sizeof sensor_data[sd->sensor].data4); + sensor = &sensor_data[sd->sensor]; + reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); reg_w(gspca_dev, 0x0013); msleep(15); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, - sizeof sensor_data[sd->sensor].stream); - poll_sensor(gspca_dev); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + + if (sd->sensor == SENSOR_OM6802) + poll_sensor(gspca_dev); - /* restart on each start, just in case, sometimes regs goes wrong - * when using controls from app */ - setbrightness(gspca_dev); - setcontrast(gspca_dev); - setcolors(gspca_dev); return 0; } @@ -926,10 +929,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, sizeof sensor_data[sd->sensor].stream); - msleep(20); reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, sizeof sensor_data[sd->sensor].stream); - if (sd->sensor != SENSOR_OTHER) { + if (sd->sensor == SENSOR_OM6802) { msleep(20); reg_w(gspca_dev, 0x0309); } diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 9f243d7e311..4b44dde9f8b 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -426,7 +426,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, packet_type0, frame, data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 6, gspca_dev->width); + frame, data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 26dd155efcc..589042f6adb 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -32,14 +32,14 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 hflip; - __u8 vflip; - __u8 lightfreq; - __u8 sharpness; + u8 hflip; + u8 vflip; + u8 lightfreq; + u8 sharpness; u8 image_offset; - char bridge; + u8 bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 u8 sensor; @@ -52,6 +52,10 @@ struct sd { #define SENSOR_OV7670 6 #define SENSOR_PO1200 7 #define SENSOR_PO3130NC 8 + u8 flags; +#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ +#define FL_HFLIP 0x02 /* mirrored by default */ +#define FL_VFLIP 0x04 /* vertical flipped by default */ }; /* V4L2 controls supported by the driver */ @@ -65,7 +69,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* next 2 controls work with ov7660 and ov7670 only */ +/* next 2 controls work with some sensors only */ #define HFLIP_IDX 0 { { @@ -152,9 +156,9 @@ static const struct v4l2_pix_format vc0323_mode[] = { .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, - {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi13x0_soc only */ + {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ .bytesperline = 1280, - .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .sizeimage = 1280 * 960 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, }; @@ -188,11 +192,11 @@ static const struct v4l2_pix_format svga_mode[] = { #define OV7660_MVFP_MIRROR 0x20 #define OV7660_MVFP_VFLIP 0x10 -static const __u8 mi0360_matrix[9] = { +static const u8 mi0360_matrix[9] = { 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 }; -static const __u8 mi0360_initVGA_JPG[][4] = { +static const u8 mi0360_initVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -301,7 +305,7 @@ static const __u8 mi0360_initVGA_JPG[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {} }; -static const __u8 mi0360_initQVGA_JPG[][4] = { +static const u8 mi0360_initQVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -421,211 +425,95 @@ static const __u8 mi0360_initQVGA_JPG[][4] = { {} }; -static const __u8 mi1310_socinitVGA_JPG[][4] = { +static const u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, - {}, -}; -static const __u8 mi1310_socinitQVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, - {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, - {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, @@ -636,133 +524,335 @@ static const __u8 mi1310_socinitQVGA_JPG[][4] = { {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, {}, }; static const u8 mi1310_soc_InitSXGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x0d, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, - {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb8, 0x06, 0x00, 0xcc}, - {0xb8, 0x07, 0x05, 0xcc}, - {0xb8, 0x08, 0x00, 0xcc}, - {0xb8, 0x09, 0x04, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x11, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x70, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x60, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x37, 0x01, 0x40, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x03, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, - {0xb6, 0x05, 0x04, 0xcc}, - {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x05, 0x03, 0xcc}, + {0xb6, 0x04, 0xc0, 0xcc}, {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x18, 0x0a, 0xcc}, - {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x09, 0xcc}, + {0xb6, 0x17, 0x60, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x14, 0xcc}, - {0xb9, 0x14, 0x14, 0xcc}, - {0xb9, 0x15, 0x14, 0xcc}, - {0xb9, 0x16, 0x14, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x1e, 0xcc}, - {0xb9, 0x1a, 0x1e, 0xcc}, - {0xb9, 0x1b, 0x1e, 0xcc}, - {0xb9, 0x1c, 0x1e, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, + {0x00, 0x00, 0x80, 0xdd}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0x00, 0x00, 0x80, 0xdd}, + {0x06, 0x70, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5e, 0x6a, 0x53, 0xbb}, + {0x5f, 0x40, 0x2c, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x58, 0x00, 0x00, 0xbb}, + {0x53, 0x09, 0x03, 0xbb}, + {0x54, 0x31, 0x18, 0xbb}, + {0x55, 0x8b, 0x5f, 0xbb}, + {0x56, 0xc0, 0xa9, 0xbb}, + {0x57, 0xe0, 0xd2, 0xbb}, + {0xe1, 0x00, 0x00, 0xbb}, + {0xdc, 0x09, 0x03, 0xbb}, + {0xdd, 0x31, 0x18, 0xbb}, + {0xde, 0x8b, 0x5f, 0xbb}, + {0xdf, 0xc0, 0xa9, 0xbb}, + {0xe0, 0xe0, 0xd2, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, {} }; -static const __u8 mi1320_gamma[17] = { +static const u8 mi1320_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 mi1320_matrix[9] = { +static const u8 mi1320_matrix[9] = { 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 }; -static const __u8 mi1320_initVGA_data[][4] = { +static const u8 mi1320_initVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -841,7 +931,7 @@ static const __u8 mi1320_initVGA_data[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 mi1320_initQVGA_data[][4] = { +static const u8 mi1320_initQVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -948,7 +1038,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -958,7 +1048,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1051,7 +1141,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -1071,7 +1161,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1161,7 +1251,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x00, 0x00, 0x20, 0xdd}, {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x00, 0x00, 0x20, 0xdd}, {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, @@ -1172,7 +1262,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1230,7 +1320,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1249,15 +1339,15 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x64, 0x5e, 0x1c, 0xbb}, {} }; -static const __u8 po3130_gamma[17] = { +static const u8 po3130_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 po3130_matrix[9] = { +static const u8 po3130_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 po3130_initVGA_data[][4] = { +static const u8 po3130_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1340,7 +1430,7 @@ static const __u8 po3130_initVGA_data[][4] = { {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 po3130_rundata[][4] = { +static const u8 po3130_rundata[][4] = { {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, {0x00, 0x44, 0x40, 0xaa}, @@ -1355,7 +1445,7 @@ static const __u8 po3130_rundata[][4] = { {} }; -static const __u8 po3130_initQVGA_data[][4] = { +static const u8 po3130_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1441,121 +1531,207 @@ static const __u8 po3130_initQVGA_data[][4] = { {} }; -static const __u8 hv7131r_gamma[17] = { -/* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ - 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, - 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff +static const u8 hv7131r_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 hv7131r_matrix[9] = { +static const u8 hv7131r_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 hv7131r_initVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, +static const u8 hv7131r_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, {0xb3, 0x03, 0x0b, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0x91, 0xcc}, {0xb3, 0x00, 0x27, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x73, 0xcc}, - {0xb8, 0x00, 0x23, 0xcc}, {0x00, 0x01, 0x0c, 0xaa}, - {0x00, 0x14, 0x01, 0xaa}, {0x00, 0x15, 0xe6, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, {0x00, 0x23, 0x00, 0xaa}, - {0x00, 0x25, 0x09, 0xaa}, {0x00, 0x26, 0x27, 0xaa}, - {0x00, 0x27, 0xc0, 0xaa}, - {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, {0xb8, 0x34, 0x65, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, {0xb9, 0x00, 0x28, 0xcc}, - {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {} }; -static const __u8 hv7131r_initQVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, +static const u8 hv7131r_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x03, 0x0b, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0x91, 0xcc}, - {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x00, 0x21, 0xcc}, - {0x00, 0x01, 0x0c, 0xaa}, {0x00, 0x14, 0x01, 0xaa}, - {0x00, 0x15, 0xe6, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, - {0x00, 0x23, 0x00, 0xaa}, {0x00, 0x25, 0x01, 0xaa}, - {0x00, 0x26, 0xd4, 0xaa}, {0x00, 0x27, 0xc0, 0xaa}, - {0xbc, 0x02, 0x08, 0xcc}, - {0xbc, 0x03, 0x70, 0xcc}, {0xbc, 0x04, 0x08, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x3c, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x04, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb8, 0xfe, 0x02, 0xcc}, - {0xb8, 0xff, 0x07, 0xcc}, {0xb9, 0x00, 0x14, 0xcc}, - {0xb9, 0x01, 0x14, 0xcc}, {0xb9, 0x02, 0x14, 0xcc}, - {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x02, 0xcc}, {0xb9, 0x05, 0x05, 0xcc}, - {0xb9, 0x06, 0x0f, 0xcc}, {0xb9, 0x07, 0x0f, 0xcc}, - {0xb9, 0x08, 0x0f, 0xcc}, - {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x65, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, - {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, {0x00, 0x30, 0x18, 0xaa}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, + {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {} }; -static const __u8 ov7660_gamma[17] = { +static const u8 ov7660_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 ov7660_matrix[9] = { +static const u8 ov7660_matrix[9] = { 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 }; -static const __u8 ov7660_initVGA_data[][4] = { +static const u8 ov7660_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, @@ -1613,7 +1789,7 @@ static const __u8 ov7660_initVGA_data[][4] = { {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, {} }; -static const __u8 ov7660_initQVGA_data[][4] = { +static const u8 ov7660_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, @@ -1682,26 +1858,26 @@ static const __u8 ov7660_initQVGA_data[][4] = { {} }; -static const __u8 ov7660_50HZ[][4] = { +static const u8 ov7660_50HZ[][4] = { {0x00, 0x3b, 0x08, 0xaa}, {0x00, 0x9d, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_60HZ[][4] = { +static const u8 ov7660_60HZ[][4] = { {0x00, 0x3b, 0x00, 0xaa}, {0x00, 0x9e, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_NoFliker[][4] = { +static const u8 ov7660_NoFliker[][4] = { {0x00, 0x13, 0x87, 0xaa}, {} }; -static const __u8 ov7670_initVGA_JPG[][4] = { +static const u8 ov7670_initVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -1831,7 +2007,7 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {}, }; -static const __u8 ov7670_initQVGA_JPG[][4] = { +static const u8 ov7670_initQVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -1966,14 +2142,14 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { }; /* PO1200 - values from usbvm326.inf and ms-win trace */ -static const __u8 po1200_gamma[17] = { +static const u8 po1200_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 po1200_matrix[9] = { +static const u8 po1200_matrix[9] = { 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e }; -static const __u8 po1200_initVGA_data[][4] = { +static const u8 po1200_initVGA_data[][4] = { {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ {0xb0, 0x03, 0x19, 0xcc}, /* {0x00, 0x00, 0x33, 0xdd}, */ @@ -2276,9 +2452,9 @@ static const struct sensor_info sensor_info_data[] = { /* read 'len' bytes in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 len) + u16 req, + u16 index, + u16 len) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -2290,9 +2466,9 @@ static void reg_r(struct gspca_dev *gspca_dev, } static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index) + u16 req, + u16 value, + u16 index) { usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2342,11 +2518,18 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; int i; u16 value; const struct sensor_info *ptsensor_info; +/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */ + } + reg_r(gspca_dev, 0xa1, 0xbfcf, 1); PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]); for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { @@ -2406,17 +2589,17 @@ static void i2c_write(struct gspca_dev *gspca_dev, } static void put_tab_to_reg(struct gspca_dev *gspca_dev, - const __u8 *tab, __u8 tabsize, __u16 addr) + const u8 *tab, u8 tabsize, u16 addr) { int j; - __u16 ad = addr; + u16 ad = addr; for (j = 0; j < tabsize; j++) reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); } static void usb_exchange(struct gspca_dev *gspca_dev, - const __u8 data[][4]) + const u8 data[][4]) { struct usb_device *dev = gspca_dev->dev; int i = 0; @@ -2466,7 +2649,8 @@ static int sd_config(struct gspca_dev *gspca_dev, }; cam = &gspca_dev->cam; - sd->bridge = id->driver_info; + sd->bridge = id->driver_info >> 8; + sd->flags = id->driver_info & 0xff; sensor = vc032x_probe_sensor(gspca_dev); switch (sensor) { case -1: @@ -2519,8 +2703,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_MI1320_SOC: cam->cam_mode = bi_mode; cam->nmodes = ARRAY_SIZE(bi_mode); - cam->input_flags = V4L2_IN_ST_VFLIP | - V4L2_IN_ST_HFLIP; break; default: cam->cam_mode = vc0323_mode; @@ -2532,14 +2714,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor == SENSOR_OV7670) { - sd->hflip = 1; - sd->vflip = 1; - } + if (sd->sensor == SENSOR_OV7670) + sd->flags |= FL_HFLIP | FL_VFLIP; sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); switch (sd->sensor) { + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: case SENSOR_OV7660: case SENSOR_OV7670: case SENSOR_PO1200: @@ -2568,39 +2750,50 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -/* for OV7660 and OV7670 only */ +/* some sensors only */ static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - + u8 data[2], hflip, vflip; + + hflip = sd->hflip; + if (sd->flags & FL_HFLIP) + hflip = !hflip; + vflip = sd->vflip; + if (sd->flags & FL_VFLIP) + vflip = !vflip; switch (sd->sensor) { - case SENSOR_OV7660: - data = 1; + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: + data[0] = data[1] = 0; /* select page 0 */ + i2c_write(gspca_dev, 0xf0, data, 2); + data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01; + data[1] = 0x02 * hflip + | 0x01 * vflip; + i2c_write(gspca_dev, 0x20, data, 2); break; + case SENSOR_OV7660: case SENSOR_OV7670: - data = 7; + data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07; + data[0] |= OV7660_MVFP_MIRROR * hflip + | OV7660_MVFP_VFLIP * vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1); break; case SENSOR_PO1200: - data = 0; - i2c_write(gspca_dev, 0x03, &data, 1); - data = 0x80 * sd->hflip - | 0x40 * sd->vflip + data[0] = 0; + i2c_write(gspca_dev, 0x03, data, 1); + data[0] = 0x80 * hflip + | 0x40 * vflip | 0x06; - i2c_write(gspca_dev, 0x1e, &data, 1); - return; - default: - return; + i2c_write(gspca_dev, 0x1e, data, 1); + break; } - data |= OV7660_MVFP_MIRROR * sd->hflip - | OV7660_MVFP_VFLIP * sd->vflip; - i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); } static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 (*ov7660_freq_tb[3])[4] = + static const u8 (*ov7660_freq_tb[3])[4] = {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; if (sd->sensor != SENSOR_OV7660) @@ -2612,7 +2805,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; + u8 data; if (sd->sensor != SENSOR_PO1200) return; @@ -2625,9 +2818,9 @@ static void setsharpness(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - const __u8 (*init)[4]; - const __u8 *GammaT = NULL; - const __u8 *MatrixT = NULL; + const u8 (*init)[4]; + const u8 *GammaT = NULL; + const u8 *MatrixT = NULL; int mode; static const u8 (*mi1320_soc_init[])[4] = { mi1320_soc_InitSXGA, @@ -2635,6 +2828,13 @@ static int sd_start(struct gspca_dev *gspca_dev) mi1320_soc_InitQVGA, }; +/*fixme: back sensor only*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(gspca_dev->dev, 0x89, 0xf0ff, 0xffff); + reg_w(gspca_dev->dev, 0xa9, 0x8348, 0x000e); + reg_w(gspca_dev->dev, 0xa9, 0x0000, 0x001a); + } + /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); @@ -2737,16 +2937,23 @@ static int sd_start(struct gspca_dev *gspca_dev) put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); /* set the led on 0x0892 0x0896 */ - if (sd->sensor != SENSOR_PO1200) { - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); - msleep(100); - sethvflip(gspca_dev); - setlightfreq(gspca_dev); - } else { - setsharpness(gspca_dev); - sethvflip(gspca_dev); + switch (sd->sensor) { + case SENSOR_PO1200: + case SENSOR_HV7131R: reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + break; + case SENSOR_MI1310_SOC: + reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); + break; + default: + if (!(sd->flags & FL_SAMSUNG)) + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + break; } + msleep(100); + setsharpness(gspca_dev); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); } return 0; } @@ -2754,8 +2961,12 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; - reg_w(dev, 0x89, 0xffff, 0xffff); + if (sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else if (!(sd->flags & FL_SAMSUNG)) + reg_w(dev, 0x89, 0xffff, 0xffff); reg_w(dev, 0xa0, 0x01, 0xb301); reg_w(dev, 0xa0, 0x09, 0xb003); } @@ -2764,15 +2975,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; if (!gspca_dev->present) return; - reg_w(dev, 0x89, 0xffff, 0xffff); +/*fixme: is this useful?*/ + if (sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else if (!(sd->flags & FL_SAMSUNG)) + reg_w(dev, 0x89, 0xffff, 0xffff); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso pkt length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2872,21 +3088,12 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - default: -/* case 2: * V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; } return -EINVAL; } @@ -2906,19 +3113,23 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ +#define BF(bridge, flags) \ + .driver_info = (BRIDGE_ ## bridge << 8) \ + | (flags) static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x405b), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x15b8, 0x6001), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, + {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, + {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)}, + {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)}, + {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)}, + {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)}, + {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 3d2756f7874..cdf3357b4c9 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -7574,7 +7574,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, - .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], + .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, .start = sd_start, diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c index 06791749d1a..5a6b78b8d25 100644 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -178,24 +178,24 @@ error: int hdpvr_set_options(struct hdpvr_device *dev) { - hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); + hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); - hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, + hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, dev->options.video_input+1); - hdpvr_set_audio(dev, dev->options.audio_input+1, + hdpvr_set_audio(dev, dev->options.audio_input+1, dev->options.audio_codec); - hdpvr_set_bitrate(dev); - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + hdpvr_set_bitrate(dev); + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, dev->options.bitrate_mode); - hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); + hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); - hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); - hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); - hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); - hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); - hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); + hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); + hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); + hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); + hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); + hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); - return 0; + return 0; } diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 188bd5aea25..1c9bc94c905 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -126,7 +126,7 @@ static int device_authorization(struct hdpvr_device *dev) char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); if (!print_buf) { v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto error; + return retval; } #endif @@ -140,7 +140,7 @@ static int device_authorization(struct hdpvr_device *dev) if (ret != 46) { v4l2_err(&dev->v4l2_dev, "unexpected answer of status request, len %d\n", ret); - goto error; + goto unlock; } #ifdef HDPVR_DEBUG else { @@ -163,7 +163,7 @@ static int device_authorization(struct hdpvr_device *dev) v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n", dev->usbc_buf[1]); ret = -EINVAL; - goto error; + goto unlock; } response = dev->usbc_buf+38; @@ -188,10 +188,10 @@ static int device_authorization(struct hdpvr_device *dev) 10000); v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "magic request returned %d\n", ret); - mutex_unlock(&dev->usbc_mutex); retval = ret != 8; -error: +unlock: + mutex_unlock(&dev->usbc_mutex); return retval; } @@ -350,6 +350,7 @@ static int hdpvr_probe(struct usb_interface *interface, mutex_lock(&dev->io_mutex); if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + mutex_unlock(&dev->io_mutex); v4l2_err(&dev->v4l2_dev, "allocating transfer buffers failed\n"); goto error; @@ -381,7 +382,6 @@ static int hdpvr_probe(struct usb_interface *interface, error: if (dev) { - mutex_unlock(&dev->io_mutex); /* this frees allocated memory */ hdpvr_delete(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index c4b5d1515c1..296330a0e1e 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -127,7 +127,6 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) sizeof(i2c_adap->name)); i2c_adap->algo = &hdpvr_algo; i2c_adap->class = I2C_CLASS_TV_ANALOG; - i2c_adap->id = I2C_HW_B_HDPVR; i2c_adap->owner = THIS_MODULE; i2c_adap->dev.parent = &dev->udev->dev; diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index ccd47f57f42..2eb9dc2ebe5 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -375,6 +375,7 @@ static int hdpvr_open(struct file *file) * in resumption */ mutex_lock(&dev->io_mutex); dev->open_count++; + mutex_unlock(&dev->io_mutex); fh->dev = dev; @@ -383,7 +384,6 @@ static int hdpvr_open(struct file *file) retval = 0; err: - mutex_unlock(&dev->io_mutex); return retval; } @@ -519,8 +519,10 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) mutex_lock(&dev->io_mutex); - if (video_is_unregistered(dev->video_dev)) + if (video_is_unregistered(dev->video_dev)) { + mutex_unlock(&dev->io_mutex); return -EIO; + } if (dev->status == STATUS_IDLE) { if (hdpvr_start_streaming(dev)) { @@ -1220,6 +1222,8 @@ static const struct video_device hdpvr_video_template = { V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_60, + .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | + V4L2_STD_PAL_60, }; int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 86f2fefe1ed..247d3115a9b 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -122,12 +122,12 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, return 1; } -static inline int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); } -static inline int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); } @@ -297,7 +297,7 @@ static void ir_work(struct work_struct *work) static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { - IR_KEYTAB_TYPE *ir_codes = NULL; + struct ir_scancode_table *ir_codes = NULL; const char *name = NULL; int ir_type; struct IR_i2c *ir; @@ -322,13 +322,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Pixelview"; ir->get_key = get_key_pixelview; ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_empty; + ir_codes = &ir_codes_empty_table; break; case 0x4b: name = "PV951"; ir->get_key = get_key_pv951; ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_pv951; + ir_codes = &ir_codes_pv951_table; break; case 0x18: case 0x1a: @@ -336,36 +336,38 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->get_key = get_key_haup; ir_type = IR_TYPE_RC5; if (hauppauge == 1) { - ir_codes = ir_codes_hauppauge_new; + ir_codes = &ir_codes_hauppauge_new_table; } else { - ir_codes = ir_codes_rc5_tv; + ir_codes = &ir_codes_rc5_tv_table; } break; case 0x30: name = "KNC One"; ir->get_key = get_key_knc1; ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_empty; + ir_codes = &ir_codes_empty_table; break; case 0x6b: name = "FusionHDTV"; ir->get_key = get_key_fusionhdtv; ir_type = IR_TYPE_RC5; - ir_codes = ir_codes_fusionhdtv_mce; + ir_codes = &ir_codes_fusionhdtv_mce_table; break; case 0x7a: case 0x47: case 0x71: case 0x2d: - if (adap->id == I2C_HW_B_CX2388x) { + if (adap->id == I2C_HW_B_CX2388x || + adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ - name = "CX2388x remote"; + name = adap->id == I2C_HW_B_CX2341X ? "CX2341x remote" + : "CX2388x remote"; ir_type = IR_TYPE_RC5; ir->get_key = get_key_haup_xvr; if (hauppauge == 1) { - ir_codes = ir_codes_hauppauge_new; + ir_codes = &ir_codes_hauppauge_new_table; } else { - ir_codes = ir_codes_rc5_tv; + ir_codes = &ir_codes_rc5_tv_table; } } else { /* Handled by saa7134-input */ @@ -377,7 +379,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "AVerMedia Cardbus remote"; ir->get_key = get_key_avermedia_cardbus; ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_avermedia_cardbus; + ir_codes = &ir_codes_avermedia_cardbus_table; break; default: dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr); @@ -392,7 +394,36 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_codes = init_data->ir_codes; name = init_data->name; - ir->get_key = init_data->get_key; + if (init_data->type) + ir_type = init_data->type; + + switch (init_data->internal_get_key_func) { + case IR_KBD_GET_KEY_CUSTOM: + /* The bridge driver provided us its own function */ + ir->get_key = init_data->get_key; + break; + case IR_KBD_GET_KEY_PIXELVIEW: + ir->get_key = get_key_pixelview; + break; + case IR_KBD_GET_KEY_PV951: + ir->get_key = get_key_pv951; + break; + case IR_KBD_GET_KEY_HAUP: + ir->get_key = get_key_haup; + break; + case IR_KBD_GET_KEY_KNC1: + ir->get_key = get_key_knc1; + break; + case IR_KBD_GET_KEY_FUSIONHDTV: + ir->get_key = get_key_fusionhdtv; + break; + case IR_KBD_GET_KEY_HAUP_XVR: + ir->get_key = get_key_haup_xvr; + break; + case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: + ir->get_key = get_key_avermedia_cardbus; + break; + } } /* Make sure we are all setup before going on */ @@ -454,7 +485,8 @@ static int ir_remove(struct i2c_client *client) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ { "ir_video", 0 }, - /* IR device specific entries could be added here */ + /* IR device specific entries should be added here */ + { "ir_rx_z8f0811_haup", 0 }, { } }; diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 2883c878076..4873b6ca580 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -977,26 +977,27 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { /* ------------------------------------------------------------------------- */ -/* AVerMedia PVR-150 Plus (M113) card */ +/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */ static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_pvr150 = { .type = IVTV_CARD_AVER_PVR150PLUS, - .name = "AVerMedia PVR-150 Plus", + .name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_GPIO, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | + IVTV_HW_WM8739 | IVTV_HW_GPIO, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, - { IVTV_CARD_INPUT_SVIDEO1, 1, - CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { @@ -1004,18 +1005,66 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, - .gpio_init = { .direction = 0x0800, .initial_value = 0 }, - .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + .gpio_init = { .direction = 0xc000, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0xc000, + .tuner = 0x0000, + .linein = 0x4000, + .radio = 0x8000 }, .tuners = { - /* This card has a Partsnic PTI-5NF05 tuner */ - { .std = V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, + /* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */ + { .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 }, }, .pci_list = ivtv_pci_aver_pvr150, + /* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */ .i2c = &ivtv_i2c_radio, }; /* ------------------------------------------------------------------------- */ +/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ + +static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_aver_ultra1500mce = { + .type = IVTV_CARD_AVER_ULTRA1500MCE, + .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_GPIO, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | + IVTV_HW_WM8739 | IVTV_HW_GPIO, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + .gpio_init = { .direction = 0xc000, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0xc000, + .tuner = 0x0000, + .linein = 0x4000, + .radio = 0x8000 }, + .tuners = { + /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_aver_ultra1500mce, + .i2c = &ivtv_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + /* AVerMedia EZMaker PCI Deluxe card */ static const struct ivtv_card_pci_info ivtv_pci_aver_ezmaker[] = { @@ -1180,6 +1229,7 @@ static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_aver_ezmaker, &ivtv_card_aver_m104, &ivtv_card_buffalo, + &ivtv_card_aver_ultra1500mce, /* Variations of standard cards but with the same PCI IDs. These cards must come last in this list. */ diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index 0b8fe85fb69..e99a0a25557 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -50,7 +50,8 @@ #define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */ #define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ #define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ -#define IVTV_CARD_LAST 25 +#define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */ +#define IVTV_CARD_LAST 26 /* Variants of existing cards but with the same PCI IDs. The driver detects these based on other device information. diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index a3b77ed3f08..4a9c8ce0ecb 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/kernel.h> #include "ivtv-driver.h" #include "ivtv-cards.h" @@ -281,7 +282,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 558f8a837ff..463ec3457d7 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -186,6 +186,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" "\t\t\t25 = AverMedia M104 (not yet working)\n" "\t\t\t26 = Buffalo PC-MV5L/PCI\n" + "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); @@ -218,7 +219,7 @@ MODULE_PARM_DESC(ivtv_yuv_mode, "\t\t\tDefault: 0 (interlaced)"); MODULE_PARM_DESC(ivtv_yuv_threshold, "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" - "\t\t\tDefault: 480");; + "\t\t\tDefault: 480"); MODULE_PARM_DESC(enc_mpg_buffers, "Encoder MPG Buffers (in MB)\n" "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); @@ -245,7 +246,7 @@ MODULE_PARM_DESC(newi2c, "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); -MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); MODULE_DESCRIPTION("CX23415/CX23416 driver"); diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index 85ac707228e..aede061cae5 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -236,18 +236,6 @@ static int subdev_s_radio(struct v4l2_subdev *sd) return 0; } -static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct ivtv *itv = sd_to_ivtv(sd); - u16 mask, data; - - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.tuner; - if (mask) - write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); - return 0; -} - static int subdev_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -344,7 +332,6 @@ static const struct v4l2_subdev_core_ops subdev_core_ops = { .g_ctrl = subdev_g_ctrl, .s_ctrl = subdev_s_ctrl, .queryctrl = subdev_queryctrl, - .s_std = subdev_s_std, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index e52aa322b13..b9c71e61f7d 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -161,19 +161,19 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) return -1; if (hw == IVTV_HW_TUNER) { /* special tuner handling */ - sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, - itv->card_i2c->radio); + 0, itv->card_i2c->radio); if (sd) sd->grp_id = 1 << idx; - sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, - itv->card_i2c->demod); + 0, itv->card_i2c->demod); if (sd) sd->grp_id = 1 << idx; - sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, - itv->card_i2c->tv); + 0, itv->card_i2c->tv); if (sd) sd->grp_id = 1 << idx; return sd ? 0 : -1; @@ -181,11 +181,11 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) if (!hw_addrs[idx]) return -1; if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { - sd = v4l2_i2c_new_probed_subdev_addr(&itv->v4l2_dev, - adap, mod, type, hw_addrs[idx]); + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, + adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx])); } else { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, - adap, mod, type, hw_addrs[idx]); + adap, mod, type, hw_addrs[idx], NULL); } if (sd) sd->grp_id = 1 << idx; @@ -509,7 +509,6 @@ static struct i2c_algorithm ivtv_algo = { /* template for our-bit banger */ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .name = "ivtv i2c driver", - .id = I2C_HW_B_CX2341X, .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, @@ -560,7 +559,6 @@ static int ivtv_getsda_old(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter ivtv_i2c_adap_template = { .name = "ivtv i2c driver", - .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 15da01710ef..67699e3f2aa 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -261,8 +261,8 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) video_set_drvdata(s->vdev, s); /* Register device. First try the desired minor, then any free one. */ - if (video_register_device(s->vdev, vfl_type, num)) { - IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + if (video_register_device_no_warn(s->vdev, vfl_type, num)) { + IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->vdev); s->vdev = NULL; diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 1d66855a379..d0765bed79c 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1915,8 +1915,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev) } static struct pci_device_id meye_pci_tbl[] = { - { PCI_VENDOR_ID_KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, { } }; diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 4d794b42d6c..45388d2ce2f 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -13,13 +13,13 @@ #include <linux/i2c.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9m001 i2c address 0x5d - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define ctruct i2c_board_info objects and link to them + * from struct soc_camera_link */ /* mt9m001 selected register addresses */ #define MT9M001_CHIP_VERSION 0x00 @@ -39,6 +39,13 @@ #define MT9M001_GLOBAL_GAIN 0x35 #define MT9M001_CHIP_ENABLE 0xF1 +#define MT9M001_MAX_WIDTH 1280 +#define MT9M001_MAX_HEIGHT 1024 +#define MT9M001_MIN_WIDTH 48 +#define MT9M001_MIN_HEIGHT 32 +#define MT9M001_COLUMN_SKIP 20 +#define MT9M001_ROW_SKIP 12 + static const struct soc_camera_data_format mt9m001_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -69,12 +76,20 @@ static const struct soc_camera_data_format mt9m001_monochrome_formats[] = { }; struct mt9m001 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + unsigned int gain; + unsigned int exposure; unsigned char autoexposure; }; +static struct mt9m001 *to_mt9m001(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -109,35 +124,20 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } -static int mt9m001_init(struct soc_camera_device *icd) +static int mt9m001_init(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; - dev_dbg(icd->vdev->parent, "%s\n", __func__); + dev_dbg(&client->dev, "%s\n", __func__); - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - - /* The camera could have been already on, we reset it additionally */ - if (icl->reset) - ret = icl->reset(&client->dev); - else - ret = -ENODEV; + /* + * We don't know, whether platform provides reset, issue a soft reset + * too. This returns all registers to their default values. + */ + ret = reg_write(client, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(client, MT9M001_RESET, 0); - if (ret < 0) { - /* Either no platform reset, or platform reset failed */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); - } /* Disable chip, synchronous option update */ if (!ret) ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); @@ -145,36 +145,12 @@ static int mt9m001_init(struct soc_camera_device *icd) return ret; } -static int mt9m001_release(struct soc_camera_device *icd) +static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; - - /* Disable the chip */ - reg_write(client, MT9M001_OUTPUT_CONTROL, 0); - - if (icl->power) - icl->power(&client->dev, 0); - - return 0; -} + struct i2c_client *client = sd->priv; -static int mt9m001_start_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - - /* Switch to master "normal" mode */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, 2) < 0) - return -EIO; - return 0; -} - -static int mt9m001_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - - /* Stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, 0) < 0) + /* Switch to master "normal" mode or stop sensor readout */ + if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) return -EIO; return 0; } @@ -182,8 +158,7 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd) static int mt9m001_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; /* Only one width bit may be set */ @@ -205,8 +180,7 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); /* MT9M001 has all capture_format parameters fixed */ unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | @@ -220,13 +194,35 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int mt9m001_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_rect rect = a->c; + struct soc_camera_device *icd = client->dev.platform_data; int ret; const u16 hblank = 9, vblank = 25; + unsigned int total_h; + + if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16) + /* + * Bayer format - even number of rows for simplicity, + * but let the user play with the top row. + */ + rect.height = ALIGN(rect.height, 2); + + /* Datasheet requirement: see register description */ + rect.width = ALIGN(rect.width, 2); + rect.left = ALIGN(rect.left, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + + total_h = rect.height + icd->y_skip_top + vblank; /* Blanking and start values - default... */ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); @@ -236,66 +232,126 @@ static int mt9m001_set_crop(struct soc_camera_device *icd, /* The caller provides a supported format, as verified per * call to icd->try_fmt() */ if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect->left); + ret = reg_write(client, MT9M001_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect->top); + ret = reg_write(client, MT9M001_ROW_START, rect.top); if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1); + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); if (!ret) ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect->height + icd->y_skip_top - 1); + rect.height + icd->y_skip_top - 1); if (!ret && mt9m001->autoexposure) { - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + vblank); + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h); if (!ret) { const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (524 + (rect->height + icd->y_skip_top + - vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9m001->exposure = (524 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; } } + if (!ret) + mt9m001->rect = rect; + return ret; } -static int mt9m001_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + + a->c = mt9m001->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M001_COLUMN_SKIP; + a->bounds.top = MT9M001_ROW_SKIP; + a->bounds.width = MT9M001_MAX_WIDTH; + a->bounds.height = MT9M001_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + pix->pixelformat = mt9m001->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_crop a = { + .c = { + .left = mt9m001->rect.left, + .top = mt9m001->rect.top, + .width = pix->width, + .height = pix->height, + }, }; + int ret; /* No support for scaling so far, just crop. TODO: use skipping */ - return mt9m001_set_crop(icd, &rect); + ret = mt9m001_s_crop(sd, &a); + if (!ret) { + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + mt9m001->fourcc = pix->pixelformat; + } + + return ret; } -static int mt9m001_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; - v4l_bound_align_image(&pix->width, 48, 1280, 1, - &pix->height, 32 + icd->y_skip_top, - 1024 + icd->y_skip_top, 0, 0); + v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH, 1, + &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top, + MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0); + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16) + pix->height = ALIGN(pix->height - 1, 2); return 0; } -static int mt9m001_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9m001_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9m001->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9m001->model; @@ -305,10 +361,10 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m001_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m001_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -325,10 +381,10 @@ static int mt9m001_get_register(struct soc_camera_device *icd, return 0; } -static int mt9m001_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m001_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -381,39 +437,17 @@ static const struct v4l2_queryctrl mt9m001_controls[] = { } }; -static int mt9m001_video_probe(struct soc_camera_device *); -static void mt9m001_video_remove(struct soc_camera_device *); -static int mt9m001_get_control(struct soc_camera_device *, struct v4l2_control *); -static int mt9m001_set_control(struct soc_camera_device *, struct v4l2_control *); - static struct soc_camera_ops mt9m001_ops = { - .owner = THIS_MODULE, - .probe = mt9m001_video_probe, - .remove = mt9m001_video_remove, - .init = mt9m001_init, - .release = mt9m001_release, - .start_capture = mt9m001_start_capture, - .stop_capture = mt9m001_stop_capture, - .set_crop = mt9m001_set_crop, - .set_fmt = mt9m001_set_fmt, - .try_fmt = mt9m001_try_fmt, .set_bus_param = mt9m001_set_bus_param, .query_bus_param = mt9m001_query_bus_param, .controls = mt9m001_controls, .num_controls = ARRAY_SIZE(mt9m001_controls), - .get_control = mt9m001_get_control, - .set_control = mt9m001_set_control, - .get_chip_id = mt9m001_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9m001_get_register, - .set_register = mt9m001_set_register, -#endif }; -static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); int data; switch (ctrl->id) { @@ -426,14 +460,21 @@ static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_contro case V4L2_CID_EXPOSURE_AUTO: ctrl->value = mt9m001->autoexposure; break; + case V4L2_CID_GAIN: + ctrl->value = mt9m001->gain; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = mt9m001->exposure; + break; } return 0; } -static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_device *icd = client->dev.platform_data; const struct v4l2_queryctrl *qctrl; int data; @@ -460,7 +501,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro unsigned long range = qctrl->default_value - qctrl->minimum; data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; - dev_dbg(&icd->dev, "Setting gain %d\n", data); + dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) return -EIO; @@ -478,7 +519,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro else data = ((gain - 64) * 7 + 28) / 56 + 96; - dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + dev_dbg(&client->dev, "Setting gain from %d to %d\n", reg_read(client, MT9M001_GLOBAL_GAIN), data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) @@ -486,7 +527,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro } /* Success */ - icd->gain = ctrl->value; + mt9m001->gain = ctrl->value; break; case V4L2_CID_EXPOSURE: /* mt9m001 has maximum == default */ @@ -497,23 +538,27 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 + range / 2) / range + 1; - dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n", - reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); + dev_dbg(&client->dev, + "Setting shutter width from %d to %lu\n", + reg_read(client, MT9M001_SHUTTER_WIDTH), + shutter); if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; + mt9m001->exposure = ctrl->value; mt9m001->autoexposure = 0; } break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->value) { const u16 vblank = 25; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, icd->height + - icd->y_skip_top + vblank) < 0) + unsigned int total_h = mt9m001->rect.height + + icd->y_skip_top + vblank; + if (reg_write(client, MT9M001_SHUTTER_WIDTH, + total_h) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (524 + (icd->height + icd->y_skip_top + vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9m001->exposure = (524 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; mt9m001->autoexposure = 1; } else @@ -525,14 +570,14 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro /* Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_device *icd) +static int mt9m001_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; - int ret; unsigned long flags; + int ret; /* We must have a parent by now. And it cannot be a wrong one. * So this entire test is completely redundant. */ @@ -542,7 +587,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); - dev_dbg(&icd->dev, "write: %d\n", data); + dev_dbg(&client->dev, "write: %d\n", data); /* Read out the chip version register */ data = reg_read(client, MT9M001_CHIP_VERSION); @@ -559,10 +604,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) icd->formats = mt9m001_monochrome_formats; break; default: - ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9M001 chip detected, register read %x\n", data); - goto ei2c; + return -ENODEV; } icd->num_formats = 0; @@ -585,42 +629,72 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) if (flags & SOCAM_DATAWIDTH_8) icd->num_formats++; - dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + mt9m001->fourcc = icd->formats->fourcc; + + dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, data == 0x8431 ? "C12STM" : "C12ST"); - /* Now that we know the model, we can start video */ - ret = soc_camera_video_start(icd); - if (ret) - goto eisis; + ret = mt9m001_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); - return 0; + /* mt9m001_init() has reset the chip, returning registers to defaults */ + mt9m001->gain = 64; + mt9m001->exposure = 255; -eisis: -ei2c: return ret; } static void mt9m001_video_remove(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, + dev_dbg(&icd->dev, "Video removed: %p, %p\n", icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); if (icl->free_bus) icl->free_bus(icl); } +static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { + .g_ctrl = mt9m001_g_ctrl, + .s_ctrl = mt9m001_s_ctrl, + .g_chip_ident = mt9m001_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m001_g_register, + .s_register = mt9m001_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { + .s_stream = mt9m001_s_stream, + .s_fmt = mt9m001_s_fmt, + .g_fmt = mt9m001_g_fmt, + .try_fmt = mt9m001_try_fmt, + .s_crop = mt9m001_s_crop, + .g_crop = mt9m001_g_crop, + .cropcap = mt9m001_cropcap, +}; + +static struct v4l2_subdev_ops mt9m001_subdev_ops = { + .core = &mt9m001_subdev_core_ops, + .video = &mt9m001_subdev_video_ops, +}; + static int mt9m001_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m001 *mt9m001; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9M001: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; @@ -636,43 +710,40 @@ static int mt9m001_probe(struct i2c_client *client, if (!mt9m001) return -ENOMEM; - mt9m001->client = client; - i2c_set_clientdata(client, mt9m001); + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); /* Second stage probe - when a capture adapter is there */ - icd = &mt9m001->icd; - icd->ops = &mt9m001_ops; - icd->control = &client->dev; - icd->x_min = 20; - icd->y_min = 12; - icd->x_current = 20; - icd->y_current = 12; - icd->width_min = 48; - icd->width_max = 1280; - icd->height_min = 32; - icd->height_max = 1024; - icd->y_skip_top = 1; - icd->iface = icl->bus_id; + icd->ops = &mt9m001_ops; + icd->y_skip_top = 0; + + mt9m001->rect.left = MT9M001_COLUMN_SKIP; + mt9m001->rect.top = MT9M001_ROW_SKIP; + mt9m001->rect.width = MT9M001_MAX_WIDTH; + mt9m001->rect.height = MT9M001_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9m001->autoexposure = 1; - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - - return 0; + ret = mt9m001_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9m001); + } -eisdr: - kfree(mt9m001); return ret; } static int mt9m001_remove(struct i2c_client *client) { - struct mt9m001 *mt9m001 = i2c_get_clientdata(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9m001->icd); + icd->ops = NULL; + mt9m001_video_remove(icd); + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9m001); return 0; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index fc5e2de0376..90da699601e 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -148,12 +148,12 @@ enum mt9m111_context { }; struct mt9m111 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ enum mt9m111_context context; struct v4l2_rect rect; u32 pixfmt; + unsigned int gain; unsigned char autoexposure; unsigned char datawidth; unsigned int powered:1; @@ -166,6 +166,11 @@ struct mt9m111 { unsigned int autowhitebalance:1; }; +static struct mt9m111 *to_mt9m111(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m111, subdev); +} + static int reg_page_map_set(struct i2c_client *client, const u16 reg) { int ret; @@ -190,7 +195,7 @@ static int mt9m111_reg_read(struct i2c_client *client, const u16 reg) ret = reg_page_map_set(client, reg); if (!ret) - ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); + ret = swab16(i2c_smbus_read_word_data(client, reg & 0xff)); dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); return ret; @@ -203,7 +208,7 @@ static int mt9m111_reg_write(struct i2c_client *client, const u16 reg, ret = reg_page_map_set(client, reg); if (!ret) - ret = i2c_smbus_write_word_data(client, (reg & 0xff), + ret = i2c_smbus_write_word_data(client, reg & 0xff, swab16(data)); dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); return ret; @@ -229,10 +234,9 @@ static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg, return mt9m111_reg_write(client, reg, ret & ~data); } -static int mt9m111_set_context(struct soc_camera_device *icd, +static int mt9m111_set_context(struct i2c_client *client, enum mt9m111_context ctxt) { - struct i2c_client *client = to_i2c_client(icd->control); int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B @@ -246,17 +250,16 @@ static int mt9m111_set_context(struct soc_camera_device *icd, return reg_write(CONTEXT_CONTROL, valA); } -static int mt9m111_setup_rect(struct soc_camera_device *icd, +static int mt9m111_setup_rect(struct i2c_client *client, struct v4l2_rect *rect) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret, is_raw_format; int width = rect->width; int height = rect->height; - if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) - || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) is_raw_format = 1; else is_raw_format = 0; @@ -292,9 +295,8 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd, return ret; } -static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) +static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt) { - struct i2c_client *client = to_i2c_client(icd->control); int ret; ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); @@ -303,19 +305,19 @@ static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) return ret; } -static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd) +static int mt9m111_setfmt_bayer8(struct i2c_client *client) { - return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER); + return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER); } -static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd) +static int mt9m111_setfmt_bayer10(struct i2c_client *client) { - return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP); + return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP); } -static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) +static int mt9m111_setfmt_rgb565(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_rgb_red_blue) @@ -324,12 +326,12 @@ static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) +static int mt9m111_setfmt_rgb555(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_rgb_red_blue) @@ -338,12 +340,12 @@ static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) +static int mt9m111_setfmt_yuv(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_yuv_cb_cr) @@ -351,52 +353,22 @@ static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) if (mt9m111->swap_yuv_y_chromas) val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_enable(struct soc_camera_device *icd) +static int mt9m111_enable(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE); if (!ret) mt9m111->powered = 1; return ret; } -static int mt9m111_disable(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = client->dev.platform_data; - int ret; - - ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); - if (!ret) - mt9m111->powered = 0; - - if (icl->power) - icl->power(&client->dev, 0); - - return ret; -} - -static int mt9m111_reset(struct soc_camera_device *icd) +static int mt9m111_reset(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); @@ -406,26 +378,12 @@ static int mt9m111_reset(struct soc_camera_device *icd) ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE | MT9M111_RESET_RESET_SOC); - if (icl->reset) - icl->reset(&client->dev); - return ret; } -static int mt9m111_start_capture(struct soc_camera_device *icd) -{ - return 0; -} - -static int mt9m111_stop_capture(struct soc_camera_device *icd) -{ - return 0; -} - static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; @@ -438,62 +396,126 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) return 0; } -static int mt9m111_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9m111_make_rect(struct i2c_client *client, + struct v4l2_rect *rect) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); + + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) { + /* Bayer format - even size lengths */ + rect->width = ALIGN(rect->width, 2); + rect->height = ALIGN(rect->height, 2); + /* Let the user play with the starting pixel */ + } + + /* FIXME: the datasheet doesn't specify minimum sizes */ + soc_camera_limit_side(&rect->left, &rect->width, + MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH); + + soc_camera_limit_side(&rect->top, &rect->height, + MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT); + + return mt9m111_setup_rect(client, rect); +} + +static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; - dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect->left, rect->top, rect->width, - rect->height); + dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(icd, rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) - mt9m111->rect = *rect; + mt9m111->rect = rect; return ret; } -static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) +static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + + a->c = mt9m111->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M111_MIN_DARK_COLS; + a->bounds.top = MT9M111_MIN_DARK_ROWS; + a->bounds.width = MT9M111_MAX_WIDTH; + a->bounds.height = MT9M111_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m111_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m111->rect.width; + pix->height = mt9m111->rect.height; + pix->pixelformat = mt9m111->pixfmt; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9m111_set_pixfmt(struct i2c_client *client, u32 pixfmt) +{ + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; switch (pixfmt) { case V4L2_PIX_FMT_SBGGR8: - ret = mt9m111_setfmt_bayer8(icd); + ret = mt9m111_setfmt_bayer8(client); break; case V4L2_PIX_FMT_SBGGR16: - ret = mt9m111_setfmt_bayer10(icd); + ret = mt9m111_setfmt_bayer10(client); break; case V4L2_PIX_FMT_RGB555: - ret = mt9m111_setfmt_rgb555(icd); + ret = mt9m111_setfmt_rgb555(client); break; case V4L2_PIX_FMT_RGB565: - ret = mt9m111_setfmt_rgb565(icd); + ret = mt9m111_setfmt_rgb565(client); break; case V4L2_PIX_FMT_UYVY: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 0; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_VYUY: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 1; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_YUYV: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 0; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_YVYU: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 1; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; default: - dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt); + dev_err(&client->dev, "Pixel format not handled : %x\n", + pixfmt); ret = -EINVAL; } @@ -503,10 +525,10 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) return ret; } -static int mt9m111_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_rect rect = { .left = mt9m111->rect.left, @@ -516,40 +538,56 @@ static int mt9m111_set_fmt(struct soc_camera_device *icd, }; int ret; - dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", - __func__, pix->pixelformat, rect.left, rect.top, rect.width, - rect.height); + dev_dbg(&client->dev, + "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", __func__, + pix->pixelformat, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(icd, &rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) - ret = mt9m111_set_pixfmt(icd, pix->pixelformat); + ret = mt9m111_set_pixfmt(client, pix->pixelformat); if (!ret) mt9m111->rect = rect; return ret; } -static int mt9m111_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; + bool bayer = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16; + + /* + * With Bayer format enforce even side lengths, but let the user play + * with the starting pixel + */ if (pix->height > MT9M111_MAX_HEIGHT) pix->height = MT9M111_MAX_HEIGHT; + else if (pix->height < 2) + pix->height = 2; + else if (bayer) + pix->height = ALIGN(pix->height, 2); + if (pix->width > MT9M111_MAX_WIDTH) pix->width = MT9M111_MAX_WIDTH; + else if (pix->width < 2) + pix->width = 2; + else if (bayer) + pix->width = ALIGN(pix->width, 2); return 0; } -static int mt9m111_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9m111_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9m111->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9m111->model; @@ -559,11 +597,11 @@ static int mt9m111_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m111_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m111_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { + struct i2c_client *client = sd->priv; int val; - struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; @@ -580,10 +618,10 @@ static int mt9m111_get_register(struct soc_camera_device *icd, return 0; } -static int mt9m111_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m111_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; @@ -635,45 +673,21 @@ static const struct v4l2_queryctrl mt9m111_controls[] = { } }; -static int mt9m111_video_probe(struct soc_camera_device *); -static void mt9m111_video_remove(struct soc_camera_device *); -static int mt9m111_get_control(struct soc_camera_device *, - struct v4l2_control *); -static int mt9m111_set_control(struct soc_camera_device *, - struct v4l2_control *); static int mt9m111_resume(struct soc_camera_device *icd); -static int mt9m111_init(struct soc_camera_device *icd); -static int mt9m111_release(struct soc_camera_device *icd); +static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state); static struct soc_camera_ops mt9m111_ops = { - .owner = THIS_MODULE, - .probe = mt9m111_video_probe, - .remove = mt9m111_video_remove, - .init = mt9m111_init, + .suspend = mt9m111_suspend, .resume = mt9m111_resume, - .release = mt9m111_release, - .start_capture = mt9m111_start_capture, - .stop_capture = mt9m111_stop_capture, - .set_crop = mt9m111_set_crop, - .set_fmt = mt9m111_set_fmt, - .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, .set_bus_param = mt9m111_set_bus_param, .controls = mt9m111_controls, .num_controls = ARRAY_SIZE(mt9m111_controls), - .get_control = mt9m111_get_control, - .set_control = mt9m111_set_control, - .get_chip_id = mt9m111_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9m111_get_register, - .set_register = mt9m111_set_register, -#endif }; -static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) +static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (mt9m111->context == HIGHPOWER) { @@ -691,9 +705,8 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) return ret; } -static int mt9m111_get_global_gain(struct soc_camera_device *icd) +static int mt9m111_get_global_gain(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); int data; data = reg_read(GLOBAL_GAIN); @@ -703,15 +716,15 @@ static int mt9m111_get_global_gain(struct soc_camera_device *icd) return data; } -static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) +static int mt9m111_set_global_gain(struct i2c_client *client, int gain) { - struct i2c_client *client = to_i2c_client(icd->control); + struct mt9m111 *mt9m111 = to_mt9m111(client); u16 val; if (gain > 63 * 2 * 2) return -EINVAL; - icd->gain = gain; + mt9m111->gain = gain; if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) val = (1 << 10) | (1 << 9) | (gain / 4); else if ((gain >= 64) && (gain < 64 * 2)) @@ -722,10 +735,9 @@ static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) return reg_write(GLOBAL_GAIN, val); } -static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) +static int mt9m111_set_autoexposure(struct i2c_client *client, int on) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (on) @@ -739,10 +751,9 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) return ret; } -static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) +static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (on) @@ -756,11 +767,10 @@ static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) return ret; } -static int mt9m111_get_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); int data; switch (ctrl->id) { @@ -785,7 +795,7 @@ static int mt9m111_get_control(struct soc_camera_device *icd, ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS); break; case V4L2_CID_GAIN: - data = mt9m111_get_global_gain(icd); + data = mt9m111_get_global_gain(client); if (data < 0) return data; ctrl->value = data; @@ -800,37 +810,36 @@ static int mt9m111_get_control(struct soc_camera_device *icd, return 0; } -static int mt9m111_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); const struct v4l2_queryctrl *qctrl; int ret; qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id); - if (!qctrl) return -EINVAL; switch (ctrl->id) { case V4L2_CID_VFLIP: mt9m111->vflip = ctrl->value; - ret = mt9m111_set_flip(icd, ctrl->value, + ret = mt9m111_set_flip(client, ctrl->value, MT9M111_RMB_MIRROR_ROWS); break; case V4L2_CID_HFLIP: mt9m111->hflip = ctrl->value; - ret = mt9m111_set_flip(icd, ctrl->value, + ret = mt9m111_set_flip(client, ctrl->value, MT9M111_RMB_MIRROR_COLS); break; case V4L2_CID_GAIN: - ret = mt9m111_set_global_gain(icd, ctrl->value); + ret = mt9m111_set_global_gain(client, ctrl->value); break; case V4L2_CID_EXPOSURE_AUTO: - ret = mt9m111_set_autoexposure(icd, ctrl->value); + ret = mt9m111_set_autoexposure(client, ctrl->value); break; case V4L2_CID_AUTO_WHITE_BALANCE: - ret = mt9m111_set_autowhitebalance(icd, ctrl->value); + ret = mt9m111_set_autowhitebalance(client, ctrl->value); break; default: ret = -EINVAL; @@ -839,62 +848,62 @@ static int mt9m111_set_control(struct soc_camera_device *icd, return ret; } -static int mt9m111_restore_state(struct soc_camera_device *icd) +static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - - mt9m111_set_context(icd, mt9m111->context); - mt9m111_set_pixfmt(icd, mt9m111->pixfmt); - mt9m111_setup_rect(icd, &mt9m111->rect); - mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); - mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); - mt9m111_set_global_gain(icd, icd->gain); - mt9m111_set_autoexposure(icd, mt9m111->autoexposure); - mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9m111 *mt9m111 = to_mt9m111(client); + + mt9m111->gain = mt9m111_get_global_gain(client); + + return 0; +} + +static int mt9m111_restore_state(struct i2c_client *client) +{ + struct mt9m111 *mt9m111 = to_mt9m111(client); + + mt9m111_set_context(client, mt9m111->context); + mt9m111_set_pixfmt(client, mt9m111->pixfmt); + mt9m111_setup_rect(client, &mt9m111->rect); + mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); + mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); + mt9m111_set_global_gain(client, mt9m111->gain); + mt9m111_set_autoexposure(client, mt9m111->autoexposure); + mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance); return 0; } static int mt9m111_resume(struct soc_camera_device *icd) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret = 0; if (mt9m111->powered) { - ret = mt9m111_enable(icd); + ret = mt9m111_enable(client); if (!ret) - ret = mt9m111_reset(icd); + ret = mt9m111_reset(client); if (!ret) - ret = mt9m111_restore_state(icd); + ret = mt9m111_restore_state(client); } return ret; } -static int mt9m111_init(struct soc_camera_device *icd) +static int mt9m111_init(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; mt9m111->context = HIGHPOWER; - ret = mt9m111_enable(icd); + ret = mt9m111_enable(client); if (!ret) - ret = mt9m111_reset(icd); + ret = mt9m111_reset(client); if (!ret) - ret = mt9m111_set_context(icd, mt9m111->context); + ret = mt9m111_set_context(client, mt9m111->context); if (!ret) - ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure); if (ret) - dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret); - return ret; -} - -static int mt9m111_release(struct soc_camera_device *icd) -{ - int ret; - - ret = mt9m111_disable(icd); - if (ret < 0) - dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret); - + dev_err(&client->dev, "mt9m11x init failed: %d\n", ret); return ret; } @@ -902,10 +911,10 @@ static int mt9m111_release(struct soc_camera_device *icd) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m111_video_probe(struct soc_camera_device *icd) +static int mt9m111_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); s32 data; int ret; @@ -917,10 +926,13 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) to_soc_camera_host(icd->dev.parent)->nr != icd->iface) return -ENODEV; - ret = mt9m111_enable(icd); - if (ret) - goto ei2c; - ret = mt9m111_reset(icd); + mt9m111->autoexposure = 1; + mt9m111->autowhitebalance = 1; + + mt9m111->swap_rgb_even_odd = 1; + mt9m111->swap_rgb_red_blue = 1; + + ret = mt9m111_init(client); if (ret) goto ei2c; @@ -935,7 +947,7 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) break; default: ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9M11x chip detected, register read %x\n", data); goto ei2c; } @@ -943,42 +955,51 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) icd->formats = mt9m111_colour_formats; icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); - dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data); + dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data); - ret = soc_camera_video_start(icd); - if (ret) - goto eisis; - - mt9m111->autoexposure = 1; - mt9m111->autowhitebalance = 1; - - mt9m111->swap_rgb_even_odd = 1; - mt9m111->swap_rgb_red_blue = 1; - - return 0; -eisis: ei2c: return ret; } -static void mt9m111_video_remove(struct soc_camera_device *icd) -{ - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); +static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { + .g_ctrl = mt9m111_g_ctrl, + .s_ctrl = mt9m111_s_ctrl, + .g_chip_ident = mt9m111_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m111_g_register, + .s_register = mt9m111_s_register, +#endif +}; - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr, - mt9m111->icd.dev.parent, mt9m111->icd.vdev); - soc_camera_video_stop(&mt9m111->icd); -} +static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { + .s_fmt = mt9m111_s_fmt, + .g_fmt = mt9m111_g_fmt, + .try_fmt = mt9m111_try_fmt, + .s_crop = mt9m111_s_crop, + .g_crop = mt9m111_g_crop, + .cropcap = mt9m111_cropcap, +}; + +static struct v4l2_subdev_ops mt9m111_subdev_ops = { + .core = &mt9m111_subdev_core_ops, + .video = &mt9m111_subdev_video_ops, +}; static int mt9m111_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m111 *mt9m111; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M11x driver needs platform data\n"); return -EINVAL; @@ -994,38 +1015,35 @@ static int mt9m111_probe(struct i2c_client *client, if (!mt9m111) return -ENOMEM; - mt9m111->client = client; - i2c_set_clientdata(client, mt9m111); + v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); /* Second stage probe - when a capture adapter is there */ - icd = &mt9m111->icd; - icd->ops = &mt9m111_ops; - icd->control = &client->dev; - icd->x_min = MT9M111_MIN_DARK_COLS; - icd->y_min = MT9M111_MIN_DARK_ROWS; - icd->x_current = icd->x_min; - icd->y_current = icd->y_min; - icd->width_min = MT9M111_MIN_DARK_ROWS; - icd->width_max = MT9M111_MAX_WIDTH; - icd->height_min = MT9M111_MIN_DARK_COLS; - icd->height_max = MT9M111_MAX_HEIGHT; - icd->y_skip_top = 0; - icd->iface = icl->bus_id; - - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - return 0; + icd->ops = &mt9m111_ops; + icd->y_skip_top = 0; + + mt9m111->rect.left = MT9M111_MIN_DARK_COLS; + mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; + mt9m111->rect.width = MT9M111_MAX_WIDTH; + mt9m111->rect.height = MT9M111_MAX_HEIGHT; + + ret = mt9m111_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9m111); + } -eisdr: - kfree(mt9m111); return ret; } static int mt9m111_remove(struct i2c_client *client) { - struct mt9m111 *mt9m111 = i2c_get_clientdata(client); - soc_camera_device_unregister(&mt9m111->icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct soc_camera_device *icd = client->dev.platform_data; + + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9m111); return 0; diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 4207fb34267..6966f644977 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -13,13 +13,13 @@ #include <linux/i2c.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9t031 i2c address 0x5d - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define i2c_board_info and link to it from + * struct soc_camera_link */ /* mt9t031 selected register addresses */ #define MT9T031_CHIP_VERSION 0x00 @@ -47,7 +47,7 @@ #define MT9T031_MAX_HEIGHT 1536 #define MT9T031_MAX_WIDTH 2048 #define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 2 +#define MT9T031_MIN_WIDTH 18 #define MT9T031_HORIZONTAL_BLANK 142 #define MT9T031_VERTICAL_BLANK 25 #define MT9T031_COLUMN_SKIP 32 @@ -68,14 +68,21 @@ static const struct soc_camera_data_format mt9t031_colour_formats[] = { }; struct mt9t031 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ - unsigned char autoexposure; u16 xskip; u16 yskip; + unsigned int gain; + unsigned int exposure; + unsigned char autoexposure; }; +static struct mt9t031 *to_mt9t031(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -136,21 +143,10 @@ static int get_shutter(struct i2c_client *client, u32 *data) return ret < 0 ? ret : 0; } -static int mt9t031_init(struct soc_camera_device *icd) +static int mt9t031_idle(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - /* Disable chip output, synchronous option update */ ret = reg_write(client, MT9T031_RESET, 1); if (ret >= 0) @@ -158,50 +154,39 @@ static int mt9t031_init(struct soc_camera_device *icd) if (ret >= 0) ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - if (ret < 0 && icl->power) - icl->power(&client->dev, 0); - return ret >= 0 ? 0 : -EIO; } -static int mt9t031_release(struct soc_camera_device *icd) +static int mt9t031_disable(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; - /* Disable the chip */ reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - if (icl->power) - icl->power(&client->dev, 0); - return 0; } -static int mt9t031_start_capture(struct soc_camera_device *icd) +static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) { - struct i2c_client *client = to_i2c_client(icd->control); - - /* Switch to master "normal" mode */ - if (reg_set(client, MT9T031_OUTPUT_CONTROL, 2) < 0) - return -EIO; - return 0; -} + struct i2c_client *client = sd->priv; + int ret; -static int mt9t031_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); + if (enable) + /* Switch to master "normal" mode */ + ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); + else + /* Stop sensor readout */ + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - /* Stop sensor readout */ - if (reg_clear(client, MT9T031_OUTPUT_CONTROL, 2) < 0) + if (ret < 0) return -EIO; + return 0; } static int mt9t031_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); /* The caller should have queried our parameters, check anyway */ if (flags & ~MT9T031_BUS_PARAM) @@ -217,69 +202,73 @@ static int mt9t031_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - struct soc_camera_link *icl = mt9t031->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); } -/* Round up minima and round down maxima */ -static void recalculate_limits(struct soc_camera_device *icd, - u16 xskip, u16 yskip) +/* target must be _even_ */ +static u16 mt9t031_skip(s32 *source, s32 target, s32 max) { - icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip; - icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip; - icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip; - icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip; - icd->width_max = MT9T031_MAX_WIDTH / xskip; - icd->height_max = MT9T031_MAX_HEIGHT / yskip; + unsigned int skip; + + if (*source < target + target / 2) { + *source = target; + return 1; + } + + skip = min(max, *source + target / 2) / target; + if (skip > 8) + skip = 8; + *source = target * skip; + + return skip; } +/* rect is the sensor rectangle, the caller guarantees parameter validity */ static int mt9t031_set_params(struct soc_camera_device *icd, struct v4l2_rect *rect, u16 xskip, u16 yskip) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9t031 *mt9t031 = to_mt9t031(client); int ret; - u16 xbin, ybin, width, height, left, top; + u16 xbin, ybin; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - /* Make sure we don't exceed sensor limits */ - if (rect->left + rect->width > icd->width_max) - rect->left = (icd->width_max - rect->width) / 2 + icd->x_min; - - if (rect->top + rect->height > icd->height_max) - rect->top = (icd->height_max - rect->height) / 2 + icd->y_min; - - width = rect->width * xskip; - height = rect->height * yskip; - left = rect->left * xskip; - top = rect->top * yskip; - xbin = min(xskip, (u16)3); ybin = min(yskip, (u16)3); - dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n", - xskip, width, rect->width, yskip, height, rect->height); - - /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ + /* + * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. + * There is always a valid suitably aligned value. The worst case is + * xbin = 3, width = 2048. Then we will start at 36, the last read out + * pixel will be 2083, which is < 2085 - first black pixel. + * + * MT9T031 datasheet imposes window left border alignment, depending on + * the selected xskip. Failing to conform to this requirement produces + * dark horizontal stripes in the image. However, even obeying to this + * requirement doesn't eliminate the stripes in all configurations. They + * appear "locally reproducibly," but can differ between tests under + * different lighting conditions. + */ switch (xbin) { - case 2: - left = (left + 3) & ~3; + case 1: + rect->left &= ~1; break; - case 3: - left = roundup(left, 6); - } - - switch (ybin) { case 2: - top = (top + 3) & ~3; + rect->left &= ~3; break; case 3: - top = roundup(top, 6); + rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? + (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); } + rect->top &= ~1; + + dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", + xskip, yskip, rect->width, rect->height, rect->left, rect->top); + /* Disable register update, reconfigure atomically */ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); if (ret < 0) @@ -299,29 +288,30 @@ static int mt9t031_set_params(struct soc_camera_device *icd, ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } - dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top); + dev_dbg(&client->dev, "new physical left %u, top %u\n", + rect->left, rect->top); /* The caller provides a supported format, as guaranteed by * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, left); + ret = reg_write(client, MT9T031_COLUMN_START, rect->left); if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, top); + ret = reg_write(client, MT9T031_ROW_START, rect->top); if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1); + ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); if (ret >= 0) ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - height + icd->y_skip_top - 1); + rect->height + icd->y_skip_top - 1); if (ret >= 0 && mt9t031->autoexposure) { - ret = set_shutter(client, height + icd->y_skip_top + vblank); + unsigned int total_h = rect->height + icd->y_skip_top + vblank; + ret = set_shutter(client, total_h); if (ret >= 0) { const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (shutter_max / 2 + (height + - icd->y_skip_top + vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; } } @@ -330,58 +320,99 @@ static int mt9t031_set_params(struct soc_camera_device *icd, if (ret >= 0) ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); + if (ret >= 0) { + mt9t031->rect = *rect; + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + return ret < 0 ? ret : 0; } -static int mt9t031_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct v4l2_rect rect = a->c; + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; + + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - /* CROP - no change in scaling, or in limits */ - return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip); + return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031->yskip); } -static int mt9t031_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - int ret; - u16 xskip, yskip; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, - }; + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); - /* - * try_fmt has put rectangle within limits. - * S_FMT - use binning and skipping for scaling, recalculate - * limits, used for cropping - */ - /* Is this more optimal than just a division? */ - for (xskip = 8; xskip > 1; xskip--) - if (rect.width * xskip <= MT9T031_MAX_WIDTH) - break; + a->c = mt9t031->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - for (yskip = 8; yskip > 1; yskip--) - if (rect.height * yskip <= MT9T031_MAX_HEIGHT) - break; + return 0; +} - recalculate_limits(icd, xskip, yskip); +static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9T031_COLUMN_SKIP; + a->bounds.top = MT9T031_ROW_SKIP; + a->bounds.width = MT9T031_MAX_WIDTH; + a->bounds.height = MT9T031_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; - ret = mt9t031_set_params(icd, &rect, xskip, yskip); - if (!ret) { - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } + return 0; +} - return ret; +static int mt9t031_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9t031->rect.width / mt9t031->xskip; + pix->height = mt9t031->rect.height / mt9t031->yskip; + pix->pixelformat = V4L2_PIX_FMT_SGRBG10; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct v4l2_pix_format *pix = &f->fmt.pix; + u16 xskip, yskip; + struct v4l2_rect rect = mt9t031->rect; + + /* + * try_fmt has put width and height within limits. + * S_FMT: use binning and skipping for scaling + */ + xskip = mt9t031_skip(&rect.width, pix->width, MT9T031_MAX_WIDTH); + yskip = mt9t031_skip(&rect.height, pix->height, MT9T031_MAX_HEIGHT); + + /* mt9t031_set_params() doesn't change width and height */ + return mt9t031_set_params(icd, &rect, xskip, yskip); } -static int mt9t031_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +/* + * If a user window larger than sensor window is requested, we'll increase the + * sensor window. + */ +static int mt9t031_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; @@ -392,15 +423,16 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, return 0; } -static int mt9t031_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9t031_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9t031->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9t031->model; @@ -410,10 +442,10 @@ static int mt9t031_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9t031_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -429,10 +461,10 @@ static int mt9t031_get_register(struct soc_camera_device *icd, return 0; } -static int mt9t031_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9t031_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -493,39 +525,17 @@ static const struct v4l2_queryctrl mt9t031_controls[] = { } }; -static int mt9t031_video_probe(struct soc_camera_device *); -static void mt9t031_video_remove(struct soc_camera_device *); -static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *); -static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *); - static struct soc_camera_ops mt9t031_ops = { - .owner = THIS_MODULE, - .probe = mt9t031_video_probe, - .remove = mt9t031_video_remove, - .init = mt9t031_init, - .release = mt9t031_release, - .start_capture = mt9t031_start_capture, - .stop_capture = mt9t031_stop_capture, - .set_crop = mt9t031_set_crop, - .set_fmt = mt9t031_set_fmt, - .try_fmt = mt9t031_try_fmt, .set_bus_param = mt9t031_set_bus_param, .query_bus_param = mt9t031_query_bus_param, .controls = mt9t031_controls, .num_controls = ARRAY_SIZE(mt9t031_controls), - .get_control = mt9t031_get_control, - .set_control = mt9t031_set_control, - .get_chip_id = mt9t031_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9t031_get_register, - .set_register = mt9t031_set_register, -#endif }; -static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); int data; switch (ctrl->id) { @@ -544,14 +554,21 @@ static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_contro case V4L2_CID_EXPOSURE_AUTO: ctrl->value = mt9t031->autoexposure; break; + case V4L2_CID_GAIN: + ctrl->value = mt9t031->gain; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = mt9t031->exposure; + break; } return 0; } -static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; const struct v4l2_queryctrl *qctrl; int data; @@ -586,7 +603,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro unsigned long range = qctrl->default_value - qctrl->minimum; data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; - dev_dbg(&icd->dev, "Setting gain %d\n", data); + dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; @@ -606,7 +623,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", + dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", reg_read(client, MT9T031_GLOBAL_GAIN), data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) @@ -614,7 +631,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro } /* Success */ - icd->gain = ctrl->value; + mt9t031->gain = ctrl->value; break; case V4L2_CID_EXPOSURE: /* mt9t031 has maximum == default */ @@ -627,11 +644,11 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro u32 old; get_shutter(client, &old); - dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n", + dev_dbg(&client->dev, "Set shutter from %u to %u\n", old, shutter); if (set_shutter(client, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; + mt9t031->exposure = ctrl->value; mt9t031->autoexposure = 0; } break; @@ -639,13 +656,14 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro if (ctrl->value) { const u16 vblank = MT9T031_VERTICAL_BLANK; const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - if (set_shutter(client, icd->height + - icd->y_skip_top + vblank) < 0) + unsigned int total_h = mt9t031->rect.height + + icd->y_skip_top + vblank; + + if (set_shutter(client, total_h) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (shutter_max / 2 + (icd->height + - icd->y_skip_top + vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; mt9t031->autoexposure = 1; } else @@ -657,22 +675,16 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro /* Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9t031_video_probe(struct soc_camera_device *icd) +static int mt9t031_video_probe(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct soc_camera_device *icd = client->dev.platform_data; + struct mt9t031 *mt9t031 = to_mt9t031(client); s32 data; int ret; - /* We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; - /* Enable the chip */ data = reg_write(client, MT9T031_CHIP_ENABLE, 1); - dev_dbg(&icd->dev, "write: %d\n", data); + dev_dbg(&client->dev, "write: %d\n", data); /* Read out the chip version register */ data = reg_read(client, MT9T031_CHIP_VERSION); @@ -684,44 +696,64 @@ static int mt9t031_video_probe(struct soc_camera_device *icd) icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats); break; default: - ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9T031 chip detected, register read %x\n", data); - goto ei2c; + return -ENODEV; } - dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data); + dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - /* Now that we know the model, we can start video */ - ret = soc_camera_video_start(icd); - if (ret) - goto evstart; + ret = mt9t031_idle(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); - return 0; + /* mt9t031_idle() has reset the chip to default. */ + mt9t031->exposure = 255; + mt9t031->gain = 64; -evstart: -ei2c: return ret; } -static void mt9t031_video_remove(struct soc_camera_device *icd) -{ - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); +static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { + .g_ctrl = mt9t031_g_ctrl, + .s_ctrl = mt9t031_s_ctrl, + .g_chip_ident = mt9t031_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9t031_g_register, + .s_register = mt9t031_s_register, +#endif +}; - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr, - icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); -} +static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { + .s_stream = mt9t031_s_stream, + .s_fmt = mt9t031_s_fmt, + .g_fmt = mt9t031_g_fmt, + .try_fmt = mt9t031_try_fmt, + .s_crop = mt9t031_s_crop, + .g_crop = mt9t031_g_crop, + .cropcap = mt9t031_cropcap, +}; + +static struct v4l2_subdev_ops mt9t031_subdev_ops = { + .core = &mt9t031_subdev_core_ops, + .video = &mt9t031_subdev_video_ops, +}; static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9T031: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9T031 driver needs platform data\n"); return -EINVAL; @@ -737,23 +769,17 @@ static int mt9t031_probe(struct i2c_client *client, if (!mt9t031) return -ENOMEM; - mt9t031->client = client; - i2c_set_clientdata(client, mt9t031); + v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); /* Second stage probe - when a capture adapter is there */ - icd = &mt9t031->icd; - icd->ops = &mt9t031_ops; - icd->control = &client->dev; - icd->x_min = MT9T031_COLUMN_SKIP; - icd->y_min = MT9T031_ROW_SKIP; - icd->x_current = icd->x_min; - icd->y_current = icd->y_min; - icd->width_min = MT9T031_MIN_WIDTH; - icd->width_max = MT9T031_MAX_WIDTH; - icd->height_min = MT9T031_MIN_HEIGHT; - icd->height_max = MT9T031_MAX_HEIGHT; - icd->y_skip_top = 0; - icd->iface = icl->bus_id; + icd->ops = &mt9t031_ops; + icd->y_skip_top = 0; + + mt9t031->rect.left = MT9T031_COLUMN_SKIP; + mt9t031->rect.top = MT9T031_ROW_SKIP; + mt9t031->rect.width = MT9T031_MAX_WIDTH; + mt9t031->rect.height = MT9T031_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9t031->autoexposure = 1; @@ -761,24 +787,29 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->xskip = 1; mt9t031->yskip = 1; - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; + mt9t031_idle(client); - return 0; + ret = mt9t031_video_probe(client); + + mt9t031_disable(client); + + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + } -eisdr: - i2c_set_clientdata(client, NULL); - kfree(mt9t031); return ret; } static int mt9t031_remove(struct i2c_client *client) { - struct mt9t031 *mt9t031 = i2c_get_clientdata(client); + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9t031->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9t031); return 0; diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index b2260de645f..cc85f77a570 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -52,13 +52,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { .step = 1, .default_value = 0, .flags = 0, - }, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + } }; struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; unsigned xtal; + unsigned hflip:1; + unsigned vflip:1; u16 global_gain, red_bal, blue_bal; }; @@ -131,7 +152,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R0A_MT9V011_CLK_SPEED, 0x0000 }, { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, - { R20_MT9V011_READ_MODE, 0x1000 }, { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; @@ -156,7 +176,7 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } -static void calc_fps(struct v4l2_subdev *sd) +static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank, speed; @@ -179,6 +199,51 @@ static void calc_fps(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", tmp / 1000, tmp % 1000, t_time); + + if (numerator && denominator) { + *numerator = 1000; + *denominator = (u32)frames_per_ms; + } +} + +static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank; + unsigned row_time, line_time; + u64 t_time, speed; + + /* Avoid bogus calculus */ + if (!numerator || !denominator) + return 0; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + + row_time = width + 113 + hblank; + line_time = height + vblank + 1; + + t_time = core->xtal * ((u64)numerator); + /* round to the closest value */ + t_time += denominator / 2; + do_div(t_time, denominator); + + speed = t_time; + do_div(speed, row_time * line_time); + + /* Avoid having a negative value for speed */ + if (speed < 2) + speed = 0; + else + speed -= 2; + + /* Avoid speed overflow */ + if (speed > 15) + return 15; + + return (u16)speed; } static void set_res(struct v4l2_subdev *sd) @@ -207,9 +272,23 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); - calc_fps(sd); + calc_fps(sd, NULL, NULL); }; +static void set_read_mode(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned mode = 0x1000; + + if (core->hflip) + mode |= 0x4000; + + if (core->vflip) + mode |= 0x8000; + + mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); +} + static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) { int i; @@ -220,6 +299,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) set_balance(sd); set_res(sd); + set_read_mode(sd); return 0; }; @@ -240,6 +320,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: ctrl->value = core->blue_bal; return 0; + case V4L2_CID_HFLIP: + ctrl->value = core->hflip ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + ctrl->value = core->vflip ? 1 : 0; + return 0; } return -EINVAL; } @@ -288,6 +374,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: core->blue_bal = ctrl->value; break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->value; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->value; + set_read_mode(sd); + return 0; default: return -EINVAL; } @@ -322,6 +416,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + calc_fps(sd, + &cp->timeperframe.numerator, + &cp->timeperframe.denominator); + + return 0; +} + +static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + u16 speed; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + speed = calc_speed(sd, tpf->numerator, tpf->denominator); + + mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); + v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); + + /* Recalculate and update fps info */ + calc_fps(sd, &tpf->numerator, &tpf->denominator); + + return 0; +} + static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { struct v4l2_pix_format *pix = &fmt->fmt.pix; @@ -393,10 +525,13 @@ static int mt9v011_s_register(struct v4l2_subdev *sd, static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { + u16 version; struct i2c_client *client = v4l2_get_subdevdata(sd); + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, - MT9V011_VERSION); + version); } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { @@ -416,6 +551,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = { .enum_fmt = mt9v011_enum_fmt, .try_fmt = mt9v011_try_fmt, .s_fmt = mt9v011_s_fmt, + .g_parm = mt9v011_g_parm, + .s_parm = mt9v011_s_parm, }; static const struct v4l2_subdev_ops mt9v011_ops = { @@ -449,8 +586,9 @@ static int mt9v011_probe(struct i2c_client *c, /* Check if the sensor is really a MT9V011 */ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - if (version != MT9V011_VERSION) { - v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n", + if ((version != MT9V011_VERSION) && + (version != MT9V011_REV_B_VERSION)) { + v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", version); kfree(core); return -EINVAL; @@ -461,8 +599,8 @@ static int mt9v011_probe(struct i2c_client *c, core->height = 480; core->xtal = 27000000; /* Hz */ - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", + c->addr << 1, c->adapter->name, version); return 0; } diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h index 9e443ee3055..3350fd6083c 100644 --- a/drivers/media/video/mt9v011.h +++ b/drivers/media/video/mt9v011.h @@ -30,6 +30,7 @@ #define R35_MT9V011_GLOBAL_GAIN 0x35 #define RF1_MT9V011_CHIP_ENABLE 0xf1 -#define MT9V011_VERSION 0x8243 +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 #endif diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index dbdcc86ae50..995607f9d3b 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -14,13 +14,13 @@ #include <linux/delay.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define ctruct i2c_board_info objects and link to them + * from struct soc_camera_link */ static char *sensor_type; module_param(sensor_type, charp, S_IRUGO); @@ -45,7 +45,7 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); #define MT9V022_PIXEL_OPERATION_MODE 0x0f #define MT9V022_LED_OUT_CONTROL 0x1b #define MT9V022_ADC_MODE_CONTROL 0x1c -#define MT9V022_ANALOG_GAIN 0x34 +#define MT9V022_ANALOG_GAIN 0x35 #define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 #define MT9V022_PIXCLK_FV_LV 0x74 #define MT9V022_DIGITAL_TEST_PATTERN 0x7f @@ -55,6 +55,13 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); /* Progressive scan, master, defaults */ #define MT9V022_CHIP_CONTROL_DEFAULT 0x188 +#define MT9V022_MAX_WIDTH 752 +#define MT9V022_MAX_HEIGHT 480 +#define MT9V022_MIN_WIDTH 48 +#define MT9V022_MIN_HEIGHT 32 +#define MT9V022_COLUMN_SKIP 1 +#define MT9V022_ROW_SKIP 4 + static const struct soc_camera_data_format mt9v022_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -85,12 +92,18 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { }; struct mt9v022 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ u16 chip_control; }; +static struct mt9v022 *to_mt9v022(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -125,29 +138,11 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } -static int mt9v022_init(struct soc_camera_device *icd) +static int mt9v022_init(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9v022 *mt9v022 = to_mt9v022(client); int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - - /* - * The camera could have been already on, we hard-reset it additionally, - * if available. Soft reset is done in video_probe(). - */ - if (icl->reset) - icl->reset(&client->dev); - /* Almost the default mode: master, parallel, simultaneous, and an * undocumented bit 0x200, which is present in table 7, but not in 8, * plus snapshot mode to disable scan for now */ @@ -161,6 +156,10 @@ static int mt9v022_init(struct soc_camera_device *icd) /* AEC, AGC on */ ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); if (!ret) + ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); + if (!ret) + ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); + if (!ret) ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); if (!ret) /* default - auto */ @@ -171,37 +170,19 @@ static int mt9v022_init(struct soc_camera_device *icd) return ret; } -static int mt9v022_release(struct soc_camera_device *icd) +static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; - - if (icl->power) - icl->power(&mt9v022->client->dev, 0); - - return 0; -} + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); -static int mt9v022_start_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (reg_write(client, MT9V022_CHIP_CONTROL, - mt9v022->chip_control) < 0) - return -EIO; - return 0; -} + if (enable) + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + else + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; -static int mt9v022_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (reg_write(client, MT9V022_CHIP_CONTROL, - mt9v022->chip_control) < 0) + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0; } @@ -209,9 +190,9 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd) static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; @@ -255,7 +236,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, if (ret < 0) return ret; - dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", pixclk, mt9v022->chip_control); return 0; @@ -263,8 +244,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned int width_flag; if (icl->query_bus_param) @@ -280,60 +260,121 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_rect rect = a->c; + struct soc_camera_device *icd = client->dev.platform_data; int ret; + /* Bayer format - even size lengths */ + if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) { + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + /* Let the user play with the starting pixel */ + } + + soc_camera_limit_side(&rect.left, &rect.width, + MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); + /* Like in example app. Contradicts the datasheet though */ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { if (ret & 1) /* Autoexposure */ ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); else ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect->left); + ret = reg_write(client, MT9V022_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect->top); + ret = reg_write(client, MT9V022_ROW_START, rect.top); if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, - rect->width > 660 - 43 ? 43 : - 660 - rect->width); + rect.width > 660 - 43 ? 43 : + 660 - rect.width); if (!ret) ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width); + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); if (!ret) ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect->height + icd->y_skip_top); + rect.height + icd->y_skip_top); if (ret < 0) return ret; - dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); + dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); + + mt9v022->rect = rect; + + return 0; +} + +static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + + a->c = mt9v022->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } -static int mt9v022_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + a->bounds.left = MT9V022_COLUMN_SKIP; + a->bounds.top = MT9V022_ROW_SKIP; + a->bounds.width = MT9V022_MAX_WIDTH; + a->bounds.height = MT9V022_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, + + pix->width = mt9v022->rect.width; + pix->height = mt9v022->rect.height; + pix->pixelformat = mt9v022->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_crop a = { + .c = { + .left = mt9v022->rect.left, + .top = mt9v022->rect.top, + .width = pix->width, + .height = pix->height, + }, }; + int ret; /* The caller provides a supported format, as verified per call to * icd->try_fmt(), datawidth is from our supported format list */ @@ -356,30 +397,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd, } /* No support for scaling on this camera, just crop. */ - return mt9v022_set_crop(icd, &rect); + ret = mt9v022_s_crop(sd, &a); + if (!ret) { + pix->width = mt9v022->rect.width; + pix->height = mt9v022->rect.height; + mt9v022->fourcc = pix->pixelformat; + } + + return ret; } -static int mt9v022_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; + int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16; - v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */, - &pix->height, 32 + icd->y_skip_top, - 480 + icd->y_skip_top, 0, 0); + v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH, + MT9V022_MAX_WIDTH, align, + &pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top, + MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0); return 0; } -static int mt9v022_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9v022_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9v022->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9v022->model; @@ -389,10 +442,10 @@ static int mt9v022_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9v022_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -409,10 +462,10 @@ static int mt9v022_get_register(struct soc_camera_device *icd, return 0; } -static int mt9v022_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9v022_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -481,41 +534,22 @@ static const struct v4l2_queryctrl mt9v022_controls[] = { } }; -static int mt9v022_video_probe(struct soc_camera_device *); -static void mt9v022_video_remove(struct soc_camera_device *); -static int mt9v022_get_control(struct soc_camera_device *, struct v4l2_control *); -static int mt9v022_set_control(struct soc_camera_device *, struct v4l2_control *); - static struct soc_camera_ops mt9v022_ops = { - .owner = THIS_MODULE, - .probe = mt9v022_video_probe, - .remove = mt9v022_video_remove, - .init = mt9v022_init, - .release = mt9v022_release, - .start_capture = mt9v022_start_capture, - .stop_capture = mt9v022_stop_capture, - .set_crop = mt9v022_set_crop, - .set_fmt = mt9v022_set_fmt, - .try_fmt = mt9v022_try_fmt, .set_bus_param = mt9v022_set_bus_param, .query_bus_param = mt9v022_query_bus_param, .controls = mt9v022_controls, .num_controls = ARRAY_SIZE(mt9v022_controls), - .get_control = mt9v022_get_control, - .set_control = mt9v022_set_control, - .get_chip_id = mt9v022_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9v022_get_register, - .set_register = mt9v022_set_register, -#endif }; -static int mt9v022_get_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; + const struct v4l2_queryctrl *qctrl; + unsigned long range; int data; + qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); + switch (ctrl->id) { case V4L2_CID_VFLIP: data = reg_read(client, MT9V022_READ_MODE); @@ -541,19 +575,35 @@ static int mt9v022_get_control(struct soc_camera_device *icd, return -EIO; ctrl->value = !!(data & 0x2); break; + case V4L2_CID_GAIN: + data = reg_read(client, MT9V022_ANALOG_GAIN); + if (data < 0) + return -EIO; + + range = qctrl->maximum - qctrl->minimum; + ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum; + + break; + case V4L2_CID_EXPOSURE: + data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); + if (data < 0) + return -EIO; + + range = qctrl->maximum - qctrl->minimum; + ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum; + + break; } return 0; } -static int mt9v022_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { int data; - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; const struct v4l2_queryctrl *qctrl; qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); - if (!qctrl) return -EINVAL; @@ -580,12 +630,9 @@ static int mt9v022_set_control(struct soc_camera_device *icd, return -EINVAL; else { unsigned long range = qctrl->maximum - qctrl->minimum; - /* Datasheet says 16 to 64. autogain only works properly - * after setting gain to maximum 14. Larger values - * produce "white fly" noise effect. On the whole, - * manually setting analog gain does no good. */ + /* Valid values 16 to 64, 32 to 64 must be even. */ unsigned long gain = ((ctrl->value - qctrl->minimum) * - 10 + range / 2) / range + 4; + 48 + range / 2) / range + 16; if (gain >= 32) gain &= ~1; /* The user wants to set gain manually, hope, she @@ -594,11 +641,10 @@ static int mt9v022_set_control(struct soc_camera_device *icd, if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) return -EIO; - dev_info(&icd->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain); + dev_dbg(&client->dev, "Setting gain from %d to %lu\n", + reg_read(client, MT9V022_ANALOG_GAIN), gain); if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0) return -EIO; - icd->gain = ctrl->value; } break; case V4L2_CID_EXPOSURE: @@ -615,13 +661,12 @@ static int mt9v022_set_control(struct soc_camera_device *icd, if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) return -EIO; - dev_dbg(&icd->dev, "Shutter width from %d to %lu\n", + dev_dbg(&client->dev, "Shutter width from %d to %lu\n", reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), shutter); if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; } break; case V4L2_CID_AUTOGAIN: @@ -646,11 +691,11 @@ static int mt9v022_set_control(struct soc_camera_device *icd, /* Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9v022_video_probe(struct soc_camera_device *icd) +static int mt9v022_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; int ret; unsigned long flags; @@ -665,7 +710,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) /* must be 0x1311 or 0x1313 */ if (data != 0x1311 && data != 0x1313) { ret = -ENODEV; - dev_info(&icd->dev, "No MT9V022 detected, ID register 0x%x\n", + dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", data); goto ei2c; } @@ -677,7 +722,9 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) /* 15 clock cycles */ udelay(200); if (reg_read(client, MT9V022_RESET)) { - dev_err(&icd->dev, "Resetting MT9V022 failed!\n"); + dev_err(&client->dev, "Resetting MT9V022 failed!\n"); + if (ret > 0) + ret = -EIO; goto ei2c; } @@ -694,7 +741,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) } if (ret < 0) - goto eisis; + goto ei2c; icd->num_formats = 0; @@ -716,42 +763,70 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) if (flags & SOCAM_DATAWIDTH_8) icd->num_formats++; - ret = soc_camera_video_start(icd); - if (ret < 0) - goto eisis; + mt9v022->fourcc = icd->formats->fourcc; - dev_info(&icd->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? "monochrome" : "colour"); - return 0; + ret = mt9v022_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); -eisis: ei2c: return ret; } static void mt9v022_video_remove(struct soc_camera_device *icd) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, + dev_dbg(&icd->dev, "Video removed: %p, %p\n", icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); if (icl->free_bus) icl->free_bus(icl); } +static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { + .g_ctrl = mt9v022_g_ctrl, + .s_ctrl = mt9v022_s_ctrl, + .g_chip_ident = mt9v022_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v022_g_register, + .s_register = mt9v022_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { + .s_stream = mt9v022_s_stream, + .s_fmt = mt9v022_s_fmt, + .g_fmt = mt9v022_g_fmt, + .try_fmt = mt9v022_try_fmt, + .s_crop = mt9v022_s_crop, + .g_crop = mt9v022_g_crop, + .cropcap = mt9v022_cropcap, +}; + +static struct v4l2_subdev_ops mt9v022_subdev_ops = { + .core = &mt9v022_subdev_core_ops, + .video = &mt9v022_subdev_video_ops, +}; + static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9V022: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; @@ -767,40 +842,41 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; + v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); + mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - mt9v022->client = client; - i2c_set_clientdata(client, mt9v022); - - icd = &mt9v022->icd; - icd->ops = &mt9v022_ops; - icd->control = &client->dev; - icd->x_min = 1; - icd->y_min = 4; - icd->x_current = 1; - icd->y_current = 4; - icd->width_min = 48; - icd->width_max = 752; - icd->height_min = 32; - icd->height_max = 480; - icd->y_skip_top = 1; - icd->iface = icl->bus_id; - - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - return 0; + icd->ops = &mt9v022_ops; + /* + * MT9V022 _really_ corrupts the first read out line. + * TODO: verify on i.MX31 + */ + icd->y_skip_top = 1; + + mt9v022->rect.left = MT9V022_COLUMN_SKIP; + mt9v022->rect.top = MT9V022_ROW_SKIP; + mt9v022->rect.width = MT9V022_MAX_WIDTH; + mt9v022->rect.height = MT9V022_MAX_HEIGHT; + + ret = mt9v022_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9v022); + } -eisdr: - kfree(mt9v022); return ret; } static int mt9v022_remove(struct i2c_client *client) { - struct mt9v022 *mt9v022 = i2c_get_clientdata(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9v022->icd); + icd->ops = NULL; + mt9v022_video_remove(icd); + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9v022); return 0; diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 2d075205bdf..5f37952c75c 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -126,7 +126,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, { struct soc_camera_device *icd = vq->priv_data; - *size = icd->width * icd->height * + *size = icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3); if (!*count) @@ -135,7 +135,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) (*count)--; - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); return 0; } @@ -147,7 +147,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* This waits until this buffer is out of danger, i.e., until it is no @@ -165,7 +165,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); int ret; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -178,12 +178,12 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, buf->inwork = 1; if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -216,10 +216,11 @@ out: static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) { struct videobuf_buffer *vbuf = &pcdev->active->vb; + struct device *dev = pcdev->icd->dev.parent; int ret; if (unlikely(!pcdev->active)) { - dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + dev_err(dev, "DMA End IRQ with no active buffer\n"); return -EFAULT; } @@ -229,11 +230,12 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) vbuf->size, pcdev->res->start + CSIRXR, DMA_MODE_READ); if (unlikely(ret)) - dev_err(pcdev->soc_host.dev, "Failed to setup DMA sg list\n"); + dev_err(dev, "Failed to setup DMA sg list\n"); return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -241,13 +243,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx1_camera_dev *pcdev = ici->priv; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - unsigned long flags; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); - spin_lock_irqsave(&pcdev->lock, flags); - list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; @@ -264,8 +263,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, __raw_writel(temp, pcdev->base + CSICR1); } } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx1_videobuf_release(struct videobuf_queue *vq, @@ -274,22 +271,23 @@ static void mx1_videobuf_release(struct videobuf_queue *vq, struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); switch (vb->state) { case VIDEOBUF_ACTIVE: - dev_dbg(&icd->dev, "%s (active)\n", __func__); + dev_dbg(dev, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_dbg(&icd->dev, "%s (queued)\n", __func__); + dev_dbg(dev, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + dev_dbg(dev, "%s (prepared)\n", __func__); break; default: - dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + dev_dbg(dev, "%s (unknown)\n", __func__); break; } #endif @@ -329,6 +327,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, static void mx1_camera_dma_irq(int channel, void *data) { struct mx1_camera_dev *pcdev = data; + struct device *dev = pcdev->icd->dev.parent; struct mx1_buffer *buf; struct videobuf_buffer *vb; unsigned long flags; @@ -338,14 +337,14 @@ static void mx1_camera_dma_irq(int channel, void *data) imx_dma_disable(channel); if (unlikely(!pcdev->active)) { - dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + dev_err(dev, "DMA End IRQ with no active buffer\n"); goto out; } vb = &pcdev->active->vb; buf = container_of(vb, struct mx1_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->soc_host.dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); mx1_camera_wakeup(pcdev, vb, buf); @@ -366,7 +365,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx1_camera_dev *pcdev = ici->priv; - videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, ici->dev, + videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -385,8 +384,9 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev) * they get a nice Oops */ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - dev_dbg(pcdev->soc_host.dev, "System clock %lukHz, target freq %dkHz, " - "divisor %lu\n", lcdclk / 1000, mclk / 1000, div); + dev_dbg(pcdev->icd->dev.parent, + "System clock %lukHz, target freq %dkHz, divisor %lu\n", + lcdclk / 1000, mclk / 1000, div); return div; } @@ -395,7 +395,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) { unsigned int csicr1 = CSICR1_EN; - dev_dbg(pcdev->soc_host.dev, "Activate device\n"); + dev_dbg(pcdev->icd->dev.parent, "Activate device\n"); clk_enable(pcdev->clk); @@ -411,7 +411,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) { - dev_dbg(pcdev->soc_host.dev, "Deactivate device\n"); + dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n"); /* Disable all CSI interface */ __raw_writel(0x00, pcdev->base + CSICR1); @@ -432,14 +432,12 @@ static int mx1_camera_add_device(struct soc_camera_device *icd) goto ebusy; } - dev_info(&icd->dev, "MX1 Camera driver attached to camera %d\n", + dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n", icd->devnum); mx1_camera_activate(pcdev); - ret = icd->ops->init(icd); - if (!ret) - pcdev->icd = icd; + pcdev->icd = icd; ebusy: return ret; @@ -460,20 +458,20 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd) /* Stop DMA engine */ imx_dma_disable(pcdev->dma_chan); - dev_info(&icd->dev, "MX1 Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n", icd->devnum); - icd->ops->release(icd); - mx1_camera_deactivate(pcdev); pcdev->icd = NULL; } static int mx1_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { - return icd->ops->set_crop(icd, rect); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, s_crop, a); } static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) @@ -543,18 +541,19 @@ static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int mx1_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(icd->dev.parent, "Format %x not found\n", + pix->pixelformat); return -EINVAL; } - ret = icd->ops->set_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; @@ -566,10 +565,11 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd, static int mx1_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); /* TODO: limit to mx1 hardware capabilities */ /* limit to sensor capabilities */ - return icd->ops->try_fmt(icd, f); + return v4l2_subdev_call(sd, video, try_fmt, f); } static int mx1_camera_reqbufs(struct soc_camera_file *icf, @@ -741,7 +741,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = DRIVER_NAME; pcdev->soc_host.ops = &mx1_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; - pcdev->soc_host.dev = &pdev->dev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); if (err) diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index e605c076ed8..dff2e5e2d8c 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -178,7 +178,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* @@ -220,7 +220,7 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (!mx3_cam->idmac_channel[0]) return -EINVAL; - *size = icd->width * icd->height * bpp; + *size = icd->user_width * icd->user_height * bpp; if (!*count) *count = 32; @@ -241,7 +241,7 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq, struct mx3_camera_buffer *buf = container_of(vb, struct mx3_camera_buffer, vb); /* current_fmt _must_ always be set */ - size_t new_size = icd->width * icd->height * + size_t new_size = icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3); int ret; @@ -251,12 +251,12 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq, */ if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; if (vb->state != VIDEOBUF_NEEDS_INIT) free_buffer(vq, buf); @@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) } } -/* Called with .vb_lock held */ +/* + * Called with .vb_lock mutex held and + * under spinlock_irqsave(&mx3_cam->lock, ...) + */ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -346,21 +349,20 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct idmac_video_param *video = &ichan->params.video; const struct soc_camera_data_format *data_fmt = icd->current_fmt; dma_cookie_t cookie; - unsigned long flags; + + BUG_ON(!irqs_disabled()); /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); - video->out_width = icd->width; - video->out_height = icd->height; - video->out_stride = icd->width; + video->out_width = icd->user_width; + video->out_height = icd->user_height; + video->out_stride = icd->user_width; #ifdef DEBUG /* helps to see what DMA actually has written */ memset((void *)vb->baddr, 0xaa, vb->bsize); #endif - spin_lock_irqsave(&mx3_cam->lock, flags); - list_add_tail(&vb->queue, &mx3_cam->capture); if (!mx3_cam->active) { @@ -370,24 +372,24 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, vb->state = VIDEOBUF_QUEUED; } - spin_unlock_irqrestore(&mx3_cam->lock, flags); + spin_unlock_irq(&mx3_cam->lock); cookie = txd->tx_submit(txd); - dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n", + cookie, sg_dma_address(&buf->sg)); + + spin_lock_irq(&mx3_cam->lock); + if (cookie >= 0) return; /* Submit error */ vb->state = VIDEOBUF_PREPARED; - spin_lock_irqsave(&mx3_cam->lock, flags); - list_del_init(&vb->queue); if (mx3_cam->active == buf) mx3_cam->active = NULL; - - spin_unlock_irqrestore(&mx3_cam->lock, flags); } /* Called with .vb_lock held */ @@ -401,9 +403,10 @@ static void mx3_videobuf_release(struct videobuf_queue *vq, container_of(vb, struct mx3_camera_buffer, vb); unsigned long flags; - dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n", + dev_dbg(icd->dev.parent, + "Release%s DMA 0x%08x (state %d), queue %sempty\n", mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), - vb->state, list_empty(&vb->queue) ? "" : "not "); + vb->state, list_empty(&vb->queue) ? "" : "not "); spin_lock_irqsave(&mx3_cam->lock, flags); if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && !list_empty(&vb->queue)) { @@ -430,7 +433,7 @@ static void mx3_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, ici->dev, + videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, icd->dev.parent, &mx3_cam->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -483,7 +486,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_enable(mx3_cam->clk); rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); + dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); if (rate) clk_set_rate(mx3_cam->clk, rate); } @@ -493,29 +496,18 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - int ret; - if (mx3_cam->icd) { - ret = -EBUSY; - goto ebusy; - } + if (mx3_cam->icd) + return -EBUSY; mx3_camera_activate(mx3_cam, icd); - ret = icd->ops->init(icd); - if (ret < 0) { - clk_disable(mx3_cam->clk); - goto einit; - } mx3_cam->icd = icd; -einit: -ebusy: - if (!ret) - dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n", - icd->devnum); + dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n", + icd->devnum); - return ret; + return 0; } /* Called with .video_lock held */ @@ -532,13 +524,11 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) *ichan = NULL; } - icd->ops->release(icd); - clk_disable(mx3_cam->clk); mx3_cam->icd = NULL; - dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n", icd->devnum); } @@ -550,7 +540,8 @@ static bool channel_change_requested(struct soc_camera_device *icd, struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; /* Do buffers have to be re-allocated or channel re-configured? */ - return ichan && rect->width * rect->height > icd->width * icd->height; + return ichan && rect->width * rect->height > + icd->user_width * icd->user_height; } static int test_platform_param(struct mx3_camera_dev *mx3_cam, @@ -598,8 +589,8 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, *flags |= SOCAM_DATAWIDTH_4; break; default: - dev_info(mx3_cam->soc_host.dev, "Unsupported bus width %d\n", - buswidth); + dev_warn(mx3_cam->soc_host.v4l2_dev.dev, + "Unsupported bus width %d\n", buswidth); return -EINVAL; } @@ -614,7 +605,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, unsigned long bus_flags, camera_flags; int ret = test_platform_param(mx3_cam, depth, &bus_flags); - dev_dbg(ici->dev, "requested bus width %d bit: %d\n", depth, ret); + dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret); if (ret < 0) return ret; @@ -623,7 +614,8 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); if (ret < 0) - dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n", + dev_warn(icd->dev.parent, + "Flags incompatible: camera %lx, host %lx\n", camera_flags, bus_flags); return ret; @@ -637,7 +629,7 @@ static bool chan_filter(struct dma_chan *chan, void *arg) if (!rq) return false; - pdata = rq->mx3_cam->soc_host.dev->platform_data; + pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data; return rq->id == chan->chan_id && pdata->dma_dev == chan->device->dev; @@ -697,7 +689,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(icd->dev.parent, + "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -709,7 +702,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(icd->dev.parent, + "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -722,7 +716,7 @@ passthrough: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, + dev_dbg(icd->dev.parent, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -732,13 +726,13 @@ passthrough: } static void configure_geometry(struct mx3_camera_dev *mx3_cam, - struct v4l2_rect *rect) + unsigned int width, unsigned int height) { u32 ctrl, width_field, height_field; /* Setup frame size - this cannot be changed on-the-fly... */ - width_field = rect->width - 1; - height_field = rect->height - 1; + width_field = width - 1; + height_field = height - 1; csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); @@ -750,11 +744,6 @@ static void configure_geometry(struct mx3_camera_dev *mx3_cam, ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; /* Sensor does the cropping */ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); - - /* - * No need to free resources here if we fail, we'll see if we need to - * do this next time we are called - */ } static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) @@ -791,25 +780,74 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) return 0; } +/* + * FIXME: learn to use stride != width, then we can keep stride properly aligned + * and support arbitrary (even) widths. + */ +static inline void stride_align(__s32 *width) +{ + if (((*width + 7) & ~7) < 4096) + *width = (*width + 7) & ~7; + else + *width = *width & ~7; +} + +/* + * As long as we don't implement host-side cropping and scaling, we can use + * default g_crop and cropcap from soc_camera.c + */ static int mx3_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { + struct v4l2_rect *rect = &a->c; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE}; + struct v4l2_pix_format *pix = &f.fmt.pix; + int ret; - /* - * We now know pixel formats and can decide upon DMA-channel(s) - * So far only direct camera-to-memory is supported - */ - if (channel_change_requested(icd, rect)) { - int ret = acquire_dma_channel(mx3_cam); + soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); + soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); + + ret = v4l2_subdev_call(sd, video, s_crop, a); + if (ret < 0) + return ret; + + /* The capture device might have changed its output */ + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + if (pix->width & 7) { + /* Ouch! We can only handle 8-byte aligned width... */ + stride_align(&pix->width); + ret = v4l2_subdev_call(sd, video, s_fmt, &f); if (ret < 0) return ret; } - configure_geometry(mx3_cam, rect); + if (pix->width != icd->user_width || pix->height != icd->user_height) { + /* + * We now know pixel formats and can decide upon DMA-channel(s) + * So far only direct camera-to-memory is supported + */ + if (channel_change_requested(icd, rect)) { + int ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + } + + configure_geometry(mx3_cam, pix->width, pix->height); + } + + dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", + pix->width, pix->height); + + icd->user_width = pix->width; + icd->user_height = pix->height; - return icd->ops->set_crop(icd, rect); + return ret; } static int mx3_camera_set_fmt(struct soc_camera_device *icd, @@ -817,22 +855,21 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, - }; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(icd->dev.parent, "Format %x not found\n", + pix->pixelformat); return -EINVAL; } + stride_align(&pix->width); + dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height); + ret = acquire_dma_channel(mx3_cam); if (ret < 0) return ret; @@ -843,21 +880,23 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, * mxc_v4l2_s_fmt() */ - configure_geometry(mx3_cam, &rect); + configure_geometry(mx3_cam, pix->width, pix->height); - ret = icd->ops->set_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; } + dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height); + return ret; } static int mx3_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; __u32 pixfmt = pix->pixelformat; @@ -866,7 +905,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -883,7 +922,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, /* camera has to see its format, but the user the original one */ pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, try_fmt, f); pix->pixelformat = xlate->host_fmt->fourcc; field = pix->field; @@ -891,7 +930,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, if (field == V4L2_FIELD_ANY) { pix->field = V4L2_FIELD_NONE; } else if (field != V4L2_FIELD_NONE) { - dev_err(&icd->dev, "Field type %d unsupported.\n", field); + dev_err(icd->dev.parent, "Field type %d unsupported.\n", field); return -EINVAL; } @@ -930,14 +969,15 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) u32 dw, sens_conf; int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags); const struct soc_camera_format_xlate *xlate; + struct device *dev = icd->dev.parent; xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(dev, "Format %x not found\n", pixfmt); return -EINVAL; } - dev_dbg(ici->dev, "requested bus width %d bit: %d\n", + dev_dbg(dev, "requested bus width %d bit: %d\n", icd->buswidth, ret); if (ret < 0) @@ -946,9 +986,10 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", + camera_flags, bus_flags, common_flags); if (!common_flags) { - dev_dbg(ici->dev, "no common flags: camera %lx, host %lx\n", - camera_flags, bus_flags); + dev_dbg(dev, "no common flags"); return -EINVAL; } @@ -1001,8 +1042,11 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) SOCAM_DATAWIDTH_4; ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + if (ret < 0) { + dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n", + common_flags, ret); return ret; + } /* * So far only gated clock mode is supported. Add a line @@ -1054,7 +1098,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - dev_dbg(ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); + dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw); return 0; } @@ -1126,8 +1170,9 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mx3_cam->capture); spin_lock_init(&mx3_cam->lock); - base = ioremap(res->start, res->end - res->start + 1); + base = ioremap(res->start, resource_size(res)); if (!base) { + pr_err("Couldn't map %x@%x\n", resource_size(res), res->start); err = -ENOMEM; goto eioremap; } @@ -1138,7 +1183,7 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) soc_host->drv_name = MX3_CAM_DRV_NAME; soc_host->ops = &mx3_soc_camera_host_ops; soc_host->priv = mx3_cam; - soc_host->dev = &pdev->dev; + soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; err = soc_camera_host_register(soc_host); @@ -1214,3 +1259,4 @@ module_exit(mx3_camera_exit); MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 35890e8b243..3454070e63f 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -186,19 +186,19 @@ static int mxb_probe(struct saa7146_dev *dev) } mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "saa7115", "saa7111", I2C_SAA7111A); + "saa7115", "saa7111", I2C_SAA7111A, NULL); mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6420", "tea6420", I2C_TEA6420_1); + "tea6420", "tea6420", I2C_TEA6420_1, NULL); mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6420", "tea6420", I2C_TEA6420_2); + "tea6420", "tea6420", I2C_TEA6420_2, NULL); mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6415c", "tea6415c", I2C_TEA6415C); + "tea6415c", "tea6415c", I2C_TEA6415C, NULL); mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tda9840", "tda9840", I2C_TDA9840); + "tda9840", "tda9840", I2C_TDA9840, NULL); mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tuner", "tuner", I2C_TUNER); + "tuner", "tuner", I2C_TUNER, NULL); if (v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "saa5246a", "saa5246a", I2C_SAA5246A)) { + "saa5246a", "saa5246a", I2C_SAA5246A, NULL)) { printk(KERN_INFO "mxb: found teletext decoder\n"); } diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 0bce255168b..eccb40ab7fe 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -22,7 +22,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/ov772x.h> @@ -382,11 +382,10 @@ struct regval_list { }; struct ov772x_color_format { - char *name; - __u32 fourcc; - u8 dsp3; - u8 com3; - u8 com7; + const struct soc_camera_data_format *format; + u8 dsp3; + u8 com3; + u8 com7; }; struct ov772x_win_size { @@ -398,14 +397,15 @@ struct ov772x_win_size { }; struct ov772x_priv { + struct v4l2_subdev subdev; struct ov772x_camera_info *info; - struct i2c_client *client; - struct soc_camera_device icd; const struct ov772x_color_format *fmt; const struct ov772x_win_size *win; int model; - unsigned int flag_vflip:1; - unsigned int flag_hflip:1; + unsigned short flag_vflip:1; + unsigned short flag_hflip:1; + /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ + unsigned short band_filter; }; #define ENDMARKER { 0xff, 0xff } @@ -481,43 +481,43 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = { */ static const struct ov772x_color_format ov772x_cfmts[] = { { - SETFOURCC(YUYV), + .format = &ov772x_fmt_lists[0], .dsp3 = 0x0, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - SETFOURCC(YVYU), + .format = &ov772x_fmt_lists[1], .dsp3 = UV_ON, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - SETFOURCC(UYVY), + .format = &ov772x_fmt_lists[2], .dsp3 = 0x0, .com3 = 0x0, .com7 = OFMT_YUV, }, { - SETFOURCC(RGB555), + .format = &ov772x_fmt_lists[3], .dsp3 = 0x0, .com3 = SWAP_RGB, .com7 = FMT_RGB555 | OFMT_RGB, }, { - SETFOURCC(RGB555X), + .format = &ov772x_fmt_lists[4], .dsp3 = 0x0, .com3 = 0x0, .com7 = FMT_RGB555 | OFMT_RGB, }, { - SETFOURCC(RGB565), + .format = &ov772x_fmt_lists[5], .dsp3 = 0x0, .com3 = SWAP_RGB, .com7 = FMT_RGB565 | OFMT_RGB, }, { - SETFOURCC(RGB565X), + .format = &ov772x_fmt_lists[6], .dsp3 = 0x0, .com3 = 0x0, .com7 = FMT_RGB565 | OFMT_RGB, @@ -570,6 +570,15 @@ static const struct v4l2_queryctrl ov772x_controls[] = { .step = 1, .default_value = 0, }, + { + .id = V4L2_CID_BAND_STOP_FILTER, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Band-stop filter", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, }; @@ -577,6 +586,12 @@ static const struct v4l2_queryctrl ov772x_controls[] = { * general function */ +static struct ov772x_priv *to_ov772x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov772x_priv, + subdev); +} + static int ov772x_write_array(struct i2c_client *client, const struct regval_list *vals) { @@ -617,58 +632,29 @@ static int ov772x_reset(struct i2c_client *client) * soc_camera_ops function */ -static int ov772x_init(struct soc_camera_device *icd) +static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret = 0; + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); - if (priv->info->link.power) { - ret = priv->info->link.power(&priv->client->dev, 1); - if (ret < 0) - return ret; + if (!enable) { + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); + return 0; } - if (priv->info->link.reset) - ret = priv->info->link.reset(&priv->client->dev); - - return ret; -} - -static int ov772x_release(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret = 0; - - if (priv->info->link.power) - ret = priv->info->link.power(&priv->client->dev, 0); - - return ret; -} - -static int ov772x_start_capture(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - if (!priv->win || !priv->fmt) { - dev_err(&icd->dev, "norm or win select error\n"); + dev_err(&client->dev, "norm or win select error\n"); return -EPERM; } - ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0); + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); - dev_dbg(&icd->dev, - "format %s, win %s\n", priv->fmt->name, priv->win->name); + dev_dbg(&client->dev, "format %s, win %s\n", + priv->fmt->format->name, priv->win->name); return 0; } -static int ov772x_stop_capture(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; -} - static int ov772x_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { @@ -677,8 +663,9 @@ static int ov772x_set_bus_param(struct soc_camera_device *icd, static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - struct soc_camera_link *icl = &priv->info->link; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct ov772x_priv *priv = i2c_get_clientdata(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; @@ -686,10 +673,10 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int ov772x_get_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); switch (ctrl->id) { case V4L2_CID_VFLIP: @@ -698,14 +685,17 @@ static int ov772x_get_control(struct soc_camera_device *icd, case V4L2_CID_HFLIP: ctrl->value = priv->flag_hflip; break; + case V4L2_CID_BAND_STOP_FILTER: + ctrl->value = priv->band_filter; + break; } return 0; } -static int ov772x_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); int ret = 0; u8 val; @@ -715,24 +705,48 @@ static int ov772x_set_control(struct soc_camera_device *icd, priv->flag_vflip = ctrl->value; if (priv->info->flags & OV772X_FLAG_VFLIP) val ^= VFLIP_IMG; - ret = ov772x_mask_set(priv->client, COM3, VFLIP_IMG, val); + ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val); break; case V4L2_CID_HFLIP: val = ctrl->value ? HFLIP_IMG : 0x00; priv->flag_hflip = ctrl->value; if (priv->info->flags & OV772X_FLAG_HFLIP) val ^= HFLIP_IMG; - ret = ov772x_mask_set(priv->client, COM3, HFLIP_IMG, val); + ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val); + break; + case V4L2_CID_BAND_STOP_FILTER: + if ((unsigned)ctrl->value > 256) + ctrl->value = 256; + if (ctrl->value == priv->band_filter) + break; + if (!ctrl->value) { + /* Switch the filter off, it is on now */ + ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); + if (!ret) + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, 0); + } else { + /* Switch the filter on, set AEC low limit */ + val = 256 - ctrl->value; + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, BNDF_ON_OFF); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, val); + } + if (!ret) + priv->band_filter = ctrl->value; break; } return ret; } -static int ov772x_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int ov772x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); id->ident = priv->model; id->revision = 0; @@ -741,17 +755,17 @@ static int ov772x_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov772x_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int ov772x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret; + struct i2c_client *client = sd->priv; + int ret; reg->size = 1; if (reg->reg > 0xff) return -EINVAL; - ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + ret = i2c_smbus_read_byte_data(client, reg->reg); if (ret < 0) return ret; @@ -760,21 +774,20 @@ static int ov772x_get_register(struct soc_camera_device *icd, return 0; } -static int ov772x_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int ov772x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; if (reg->reg > 0xff || reg->val > 0xff) return -EINVAL; - return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); } #endif -static const struct ov772x_win_size* -ov772x_select_win(u32 width, u32 height) +static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) { __u32 diff; const struct ov772x_win_size *win; @@ -793,9 +806,10 @@ ov772x_select_win(u32 width, u32 height) return win; } -static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, - u32 pixfmt) +static int ov772x_set_params(struct i2c_client *client, + u32 *width, u32 *height, u32 pixfmt) { + struct ov772x_priv *priv = to_ov772x(client); int ret = -EINVAL; u8 val; int i; @@ -805,7 +819,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, */ priv->fmt = NULL; for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { - if (pixfmt == ov772x_cfmts[i].fourcc) { + if (pixfmt == ov772x_cfmts[i].format->fourcc) { priv->fmt = ov772x_cfmts + i; break; } @@ -816,12 +830,12 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, /* * select win */ - priv->win = ov772x_select_win(width, height); + priv->win = ov772x_select_win(*width, *height); /* * reset hardware */ - ov772x_reset(priv->client); + ov772x_reset(client); /* * Edge Ctrl @@ -835,17 +849,17 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * Remove it when manual mode. */ - ret = ov772x_mask_set(priv->client, DSPAUTO, EDGE_ACTRL, 0x00); + ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_TRSHLD, EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_STRNGT, EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) @@ -857,13 +871,13 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * * set upper and lower limit */ - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_UPPER, EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_LOWER, EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) @@ -873,7 +887,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, /* * set size format */ - ret = ov772x_write_array(priv->client, priv->win->regs); + ret = ov772x_write_array(client, priv->win->regs); if (ret < 0) goto ov772x_set_fmt_error; @@ -882,7 +896,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, */ val = priv->fmt->dsp3; if (val) { - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, DSP_CTRL3, UV_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; @@ -901,7 +915,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, if (priv->flag_hflip) val ^= HFLIP_IMG; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, COM3, SWAP_MASK | IMG_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; @@ -910,47 +924,99 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * set COM7 */ val = priv->win->com7_bit | priv->fmt->com7; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK), val); if (ret < 0) goto ov772x_set_fmt_error; + /* + * set COM8 + */ + if (priv->band_filter) { + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, 256 - priv->band_filter); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + *width = priv->win->width; + *height = priv->win->height; + return ret; ov772x_set_fmt_error: - ov772x_reset(priv->client); + ov772x_reset(client); priv->win = NULL; priv->fmt = NULL; return ret; } -static int ov772x_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + a->c.left = 0; + a->c.top = 0; + a->c.width = VGA_WIDTH; + a->c.height = VGA_HEIGHT; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (!priv->fmt) - return -EINVAL; + return 0; +} - return ov772x_set_params(priv, rect->width, rect->height, - priv->fmt->fourcc); +static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = VGA_WIDTH; + a->bounds.height = VGA_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; } -static int ov772x_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (!priv->win || !priv->fmt) { + u32 width = VGA_WIDTH, height = VGA_HEIGHT; + int ret = ov772x_set_params(client, &width, &height, + V4L2_PIX_FMT_YUYV); + if (ret < 0) + return ret; + } + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix->width = priv->win->width; + pix->height = priv->win->height; + pix->pixelformat = priv->fmt->format->fourcc; + pix->colorspace = priv->fmt->format->colorspace; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; struct v4l2_pix_format *pix = &f->fmt.pix; - return ov772x_set_params(priv, pix->width, pix->height, + return ov772x_set_params(client, &pix->width, &pix->height, pix->pixelformat); } -static int ov772x_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_try_fmt(struct v4l2_subdev *sd, + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; const struct ov772x_win_size *win; @@ -967,9 +1033,10 @@ static int ov772x_try_fmt(struct soc_camera_device *icd, return 0; } -static int ov772x_video_probe(struct soc_camera_device *icd) +static int ov772x_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct ov772x_priv *priv = to_ov772x(client); u8 pid, ver; const char *devname; @@ -986,7 +1053,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd) */ if (SOCAM_DATAWIDTH_10 != priv->info->buswidth && SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&icd->dev, "bus width error\n"); + dev_err(&client->dev, "bus width error\n"); return -ENODEV; } @@ -996,8 +1063,8 @@ static int ov772x_video_probe(struct soc_camera_device *icd) /* * check and show product ID and manufacturer ID */ - pid = i2c_smbus_read_byte_data(priv->client, PID); - ver = i2c_smbus_read_byte_data(priv->client, VER); + pid = i2c_smbus_read_byte_data(client, PID); + ver = i2c_smbus_read_byte_data(client, VER); switch (VERSION(pid, ver)) { case OV7720: @@ -1009,69 +1076,77 @@ static int ov772x_video_probe(struct soc_camera_device *icd) priv->model = V4L2_IDENT_OV7725; break; default: - dev_err(&icd->dev, + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); return -ENODEV; } - dev_info(&icd->dev, + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, - i2c_smbus_read_byte_data(priv->client, MIDH), - i2c_smbus_read_byte_data(priv->client, MIDL)); - - return soc_camera_video_start(icd); -} + i2c_smbus_read_byte_data(client, MIDH), + i2c_smbus_read_byte_data(client, MIDL)); -static void ov772x_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); + return 0; } static struct soc_camera_ops ov772x_ops = { - .owner = THIS_MODULE, - .probe = ov772x_video_probe, - .remove = ov772x_video_remove, - .init = ov772x_init, - .release = ov772x_release, - .start_capture = ov772x_start_capture, - .stop_capture = ov772x_stop_capture, - .set_crop = ov772x_set_crop, - .set_fmt = ov772x_set_fmt, - .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, .query_bus_param = ov772x_query_bus_param, .controls = ov772x_controls, .num_controls = ARRAY_SIZE(ov772x_controls), - .get_control = ov772x_get_control, - .set_control = ov772x_set_control, - .get_chip_id = ov772x_get_chip_id, +}; + +static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { + .g_ctrl = ov772x_g_ctrl, + .s_ctrl = ov772x_s_ctrl, + .g_chip_ident = ov772x_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = ov772x_get_register, - .set_register = ov772x_set_register, + .g_register = ov772x_g_register, + .s_register = ov772x_s_register, #endif }; +static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { + .s_stream = ov772x_s_stream, + .g_fmt = ov772x_g_fmt, + .s_fmt = ov772x_s_fmt, + .try_fmt = ov772x_try_fmt, + .cropcap = ov772x_cropcap, + .g_crop = ov772x_g_crop, +}; + +static struct v4l2_subdev_ops ov772x_subdev_ops = { + .core = &ov772x_subdev_core_ops, + .video = &ov772x_subdev_video_ops, +}; + /* * i2c_driver function */ static int ov772x_probe(struct i2c_client *client, - const struct i2c_device_id *did) + const struct i2c_device_id *did) { struct ov772x_priv *priv; struct ov772x_camera_info *info; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; int ret; - if (!client->dev.platform_data) + if (!icd) { + dev_err(&client->dev, "OV772X: missing soc-camera data!\n"); return -EINVAL; + } - info = container_of(client->dev.platform_data, - struct ov772x_camera_info, link); + icl = to_soc_camera_link(icd); + if (!icl) + return -EINVAL; + + info = container_of(icl, struct ov772x_camera_info, link); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, @@ -1084,20 +1159,15 @@ static int ov772x_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = info; - priv->client = client; - i2c_set_clientdata(client, priv); + priv->info = info; - icd = &priv->icd; - icd->ops = &ov772x_ops; - icd->control = &client->dev; - icd->width_max = MAX_WIDTH; - icd->height_max = MAX_HEIGHT; - icd->iface = priv->info->link.bus_id; + v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); - ret = soc_camera_device_register(icd); + icd->ops = &ov772x_ops; + ret = ov772x_video_probe(icd, client); if (ret) { + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); } @@ -1107,9 +1177,10 @@ static int ov772x_probe(struct i2c_client *client, static int ov772x_remove(struct i2c_client *client) { - struct ov772x_priv *priv = i2c_get_clientdata(client); + struct ov772x_priv *priv = to_ov772x(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&priv->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); return 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 416933ca607..cc06d5e4adc 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -65,9 +65,10 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) u32 input; pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes[sid]) != NULL) && + if ((sp != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 336a20eded0..e4d7c13cab8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -298,6 +298,7 @@ static struct tda829x_config tda829x_no_probe = { static struct tda18271_config hauppauge_tda18271_dvb_config = { .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) @@ -393,6 +394,7 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index cbc388729d7..13639b30270 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2063,8 +2063,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, return -EINVAL; } - /* Note how the 2nd and 3rd arguments are the same for both - * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev(). Why? + /* Note how the 2nd and 3rd arguments are the same for + * v4l2_i2c_new_subdev(). Why? * Well the 2nd argument is the module name to load, while the 3rd * argument is documented in the framework as being the "chipid" - * and every other place where I can find examples of this, the @@ -2077,15 +2077,15 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, mid, i2caddr[0]); sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, fname, fname, - i2caddr[0]); + i2caddr[0], NULL); } else { pvr2_trace(PVR2_TRACE_INIT, "Module ID %u:" " Setting up with address probe list", mid); - sd = v4l2_i2c_new_probed_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, + sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, fname, fname, - i2caddr); + 0, i2caddr); } if (!sd) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 610bd848df2..a334b1a966a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -540,7 +540,6 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, .class = 0, - .id = I2C_HW_B_BT848, }; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 8d17cf61330..f976df452a3 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1057,7 +1057,8 @@ static int pwc_create_sysfs_files(struct video_device *vdev) goto err; if (pdev->features & FEATURE_MOTOR_PANTILT) { rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt); - if (rc) goto err_button; + if (rc) + goto err_button; } return 0; @@ -1072,6 +1073,7 @@ err: static void pwc_remove_sysfs_files(struct video_device *vdev) { struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) device_remove_file(&vdev->dev, &dev_attr_pan_tilt); device_remove_file(&vdev->dev, &dev_attr_button); @@ -1229,13 +1231,11 @@ static void pwc_cleanup(struct pwc_device *pdev) video_unregister_device(pdev->vdev); #ifdef CONFIG_USB_PWC_INPUT_EVDEV - if (pdev->button_dev) { + if (pdev->button_dev) input_unregister_device(pdev->button_dev); - input_free_device(pdev->button_dev); - kfree(pdev->button_dev->phys); - pdev->button_dev = NULL; - } #endif + + kfree(pdev); } /* Note that all cleanup is done in the reverse order as in _open */ @@ -1281,8 +1281,6 @@ static int pwc_video_close(struct file *file) PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); } else { pwc_cleanup(pdev); - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); /* search device_hint[] table if we occupy a slot, by any chance */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) if (device_hint[hint].pdev == pdev) @@ -1499,13 +1497,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id struct usb_device *udev = interface_to_usbdev(intf); struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; - int i, hint, rc; + int hint, rc; int features = 0; int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - char *phys = NULL; -#endif vendor_id = le16_to_cpu(udev->descriptor.idVendor); product_id = le16_to_cpu(udev->descriptor.idProduct); @@ -1757,8 +1752,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vframes = default_fps; strcpy(pdev->serial, serial_number); pdev->features = features; - if (vendor_id == 0x046D && product_id == 0x08B5) - { + if (vendor_id == 0x046D && product_id == 0x08B5) { /* Logitech QuickCam Orbit The ranges have been determined experimentally; they may differ from cam to cam. Also, the exact ranges left-right and up-down are different for my cam @@ -1780,8 +1774,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev = video_device_alloc(); if (!pdev->vdev) { PWC_ERROR("Err, cannot allocate video_device struture. Failing probe."); - kfree(pdev); - return -ENOMEM; + rc = -ENOMEM; + goto err_free_mem; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); pdev->vdev->parent = &intf->dev; @@ -1806,25 +1800,23 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } pdev->vdev->release = video_device_release; - i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); - if (i < 0) { - PWC_ERROR("Failed to register as video device (%d).\n", i); - rc = i; - goto err; - } - else { - PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num); + rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (rc < 0) { + PWC_ERROR("Failed to register as video device (%d).\n", rc); + goto err_video_release; } + PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num); + /* occupy slot */ if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev); - usb_set_intfdata (intf, pdev); + usb_set_intfdata(intf, pdev); rc = pwc_create_sysfs_files(pdev->vdev); if (rc) - goto err_unreg; + goto err_video_unreg; /* Set the leds off */ pwc_set_leds(pdev, 0, 0); @@ -1835,16 +1827,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->button_dev = input_allocate_device(); if (!pdev->button_dev) { PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); - return -ENOMEM; + rc = -ENOMEM; + pwc_remove_sysfs_files(pdev->vdev); + goto err_video_unreg; } + usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys)); + strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys)); + pdev->button_dev->name = "PWC snapshot button"; - phys = kasprintf(GFP_KERNEL,"usb-%s-%s", pdev->udev->bus->bus_name, pdev->udev->devpath); - if (!phys) { - input_free_device(pdev->button_dev); - return -ENOMEM; - } - pdev->button_dev->phys = phys; + pdev->button_dev->phys = pdev->button_phys; usb_to_input_id(pdev->udev, &pdev->button_dev->id); pdev->button_dev->dev.parent = &pdev->udev->dev; pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY); @@ -1853,25 +1845,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id rc = input_register_device(pdev->button_dev); if (rc) { input_free_device(pdev->button_dev); - kfree(pdev->button_dev->phys); pdev->button_dev = NULL; - return rc; + pwc_remove_sysfs_files(pdev->vdev); + goto err_video_unreg; } #endif return 0; -err_unreg: +err_video_unreg: if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = NULL; video_unregister_device(pdev->vdev); -err: - video_device_release(pdev->vdev); /* Drip... drip... drip... */ - kfree(pdev); /* Oops, no memory leaks please */ + pdev->vdev = NULL; /* So we don't try to release it below */ +err_video_release: + video_device_release(pdev->vdev); +err_free_mem: + kfree(pdev); return rc; } -/* The user janked out the cable... */ +/* The user yanked out the cable... */ static void usb_pwc_disconnect(struct usb_interface *intf) { struct pwc_device *pdev; @@ -1902,7 +1896,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) /* Alert waiting processes */ wake_up_interruptible(&pdev->frameq); /* Wait until device is closed */ - if(pdev->vopen) { + if (pdev->vopen) { mutex_lock(&pdev->modlock); pdev->unplugged = 1; mutex_unlock(&pdev->modlock); @@ -1911,8 +1905,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) /* Device is closed, so we can safely unregister it */ PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); pwc_cleanup(pdev); - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); disconnect_out: /* search device_hint[] table if we occupy a slot, by any chance */ diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2876ce08451..bdb4ced5749 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1033,7 +1033,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (std->index != 0) return -EINVAL; std->id = V4L2_STD_UNKNOWN; - strncpy(std->name, "webcam", sizeof(std->name)); + strlcpy(std->name, "webcam", sizeof(std->name)); return 0; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 0b658dee05a..0902355dfa7 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -135,12 +135,6 @@ #define DEVICE_USE_CODEC3(x) ((x)>=700) #define DEVICE_USE_CODEC23(x) ((x)>=675) - -#ifndef V4L2_PIX_FMT_PWC1 -#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') -#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') -#endif - /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ struct pwc_iso_buf { @@ -259,6 +253,7 @@ struct pwc_device int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ #ifdef CONFIG_USB_PWC_INPUT_EVDEV struct input_dev *button_dev; /* webcam snapshot button input */ + char button_phys[64]; #endif /*** Misc. data ***/ diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 46e0d8ad880..6952e9602d5 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -225,6 +225,10 @@ struct pxa_camera_dev { u32 save_cicr[5]; }; +struct pxa_cam { + unsigned long flags; +}; + static const char *pxa_cam_driver_description = "PXA_Camera"; static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ @@ -237,9 +241,9 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, { struct soc_camera_device *icd = vq->priv_data; - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); - *size = roundup(icd->width * icd->height * + *size = roundup(icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3), 8); if (0 == *count) @@ -259,7 +263,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); /* This waits until this buffer is out of danger, i.e., until it is no @@ -270,7 +274,8 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { if (buf->dmas[i].sg_cpu) - dma_free_coherent(ici->dev, buf->dmas[i].sg_size, + dma_free_coherent(ici->v4l2_dev.dev, + buf->dmas[i].sg_size, buf->dmas[i].sg_cpu, buf->dmas[i].sg_dma); buf->dmas[i].sg_cpu = NULL; @@ -325,19 +330,20 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct scatterlist **sg_first, int *sg_first_ofs) { struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; + struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct scatterlist *sg; int i, offset, sglen; int dma_len = 0, xfer_len = 0; if (pxa_dma->sg_cpu) - dma_free_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, + dma_free_coherent(dev, pxa_dma->sg_size, pxa_dma->sg_cpu, pxa_dma->sg_dma); sglen = calculate_dma_sglen(*sg_first, dma->sglen, *sg_first_ofs, size); pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); - pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, + pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size, &pxa_dma->sg_dma, GFP_KERNEL); if (!pxa_dma->sg_cpu) return -ENOMEM; @@ -345,7 +351,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sglen = sglen; offset = *sg_first_ofs; - dev_dbg(pcdev->soc_host.dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", + dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); @@ -368,7 +374,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); - dev_vdbg(pcdev->soc_host.dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", + dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), sg_dma_address(sg) + offset, xfer_len); offset = 0; @@ -418,11 +424,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int size_y, size_u = 0, size_v = 0; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -441,12 +448,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, buf->inwork = 1; if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -480,8 +487,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for Y/RGB failed\n"); + dev_err(dev, "DMA initialization for Y/RGB failed\n"); goto fail; } @@ -490,8 +496,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, size_u, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for U failed\n"); + dev_err(dev, "DMA initialization for U failed\n"); goto fail_u; } @@ -500,8 +505,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, size_v, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for V failed\n"); + dev_err(dev, "DMA initialization for V failed\n"); goto fail_v; } @@ -514,10 +518,10 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, return 0; fail_v: - dma_free_coherent(pcdev->soc_host.dev, buf->dmas[1].sg_size, + dma_free_coherent(dev, buf->dmas[1].sg_size, buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); fail_u: - dma_free_coherent(pcdev->soc_host.dev, buf->dmas[0].sg_size, + dma_free_coherent(dev, buf->dmas[0].sg_size, buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); @@ -541,7 +545,8 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.dev, "%s (channel=%d) ddadr=%08x\n", __func__, + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d) ddadr=%08x\n", __func__, i, active->dmas[i].sg_dma); DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma; DCSR(pcdev->dma_chans[i]) = DCSR_RUN; @@ -553,7 +558,8 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.dev, "%s (channel=%d)\n", __func__, i); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d)\n", __func__, i); DCSR(pcdev->dma_chans[i]) = 0; } } @@ -589,7 +595,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) { unsigned long cicr0, cifr; - dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); /* Reset the FIFOs */ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; __raw_writel(cifr, pcdev->base + CIFR); @@ -609,9 +615,10 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) __raw_writel(cicr0, pcdev->base + CICR0); pcdev->active = NULL; - dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -619,12 +626,9 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - unsigned long flags; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, - vb, vb->baddr, vb->bsize, pcdev->active); - - spin_lock_irqsave(&pcdev->lock, flags); + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", + __func__, vb, vb->baddr, vb->bsize, pcdev->active); list_add_tail(&vb->queue, &pcdev->capture); @@ -633,8 +637,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, if (!pcdev->active) pxa_camera_start_capture(pcdev); - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void pxa_videobuf_release(struct videobuf_queue *vq, @@ -643,22 +645,23 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); switch (vb->state) { case VIDEOBUF_ACTIVE: - dev_dbg(&icd->dev, "%s (active)\n", __func__); + dev_dbg(dev, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_dbg(&icd->dev, "%s (queued)\n", __func__); + dev_dbg(dev, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + dev_dbg(dev, "%s (prepared)\n", __func__); break; default: - dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + dev_dbg(dev, "%s (unknown)\n", __func__); break; } #endif @@ -678,7 +681,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); - dev_dbg(pcdev->soc_host.dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", + __func__, vb); if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); @@ -714,7 +718,8 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) for (i = 0; i < pcdev->channels; i++) if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP) is_dma_stopped = 0; - dev_dbg(pcdev->soc_host.dev, "%s : top queued buffer=%p, dma_stopped=%d\n", + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s : top queued buffer=%p, dma_stopped=%d\n", __func__, pcdev->active, is_dma_stopped); if (pcdev->active && is_dma_stopped) pxa_camera_start_capture(pcdev); @@ -723,6 +728,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { + struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf; unsigned long flags; u32 status, camera_status, overrun; @@ -739,13 +745,13 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, overrun |= CISR_IFO_1 | CISR_IFO_2; if (status & DCSR_BUSERR) { - dev_err(pcdev->soc_host.dev, "DMA Bus Error IRQ!\n"); + dev_err(dev, "DMA Bus Error IRQ!\n"); goto out; } if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) { - dev_err(pcdev->soc_host.dev, "Unknown DMA IRQ source, " - "status: 0x%08x\n", status); + dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n", + status); goto out; } @@ -768,7 +774,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->soc_host.dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", + dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", __func__, channel, status & DCSR_STARTINTR ? "SOF " : "", status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel)); @@ -779,7 +785,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, */ if (camera_status & overrun && !list_is_last(pcdev->capture.next, &pcdev->capture)) { - dev_dbg(pcdev->soc_host.dev, "FIFO overrun! CISR: %x\n", + dev_dbg(dev, "FIFO overrun! CISR: %x\n", camera_status); pxa_camera_stop_capture(pcdev); pxa_camera_start_capture(pcdev); @@ -834,9 +840,11 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, sizeof(struct pxa_buffer), icd); } -static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) +static u32 mclk_get_divisor(struct platform_device *pdev, + struct pxa_camera_dev *pcdev) { unsigned long mclk = pcdev->mclk; + struct device *dev = &pdev->dev; u32 div; unsigned long lcdclk; @@ -846,7 +854,7 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(pcdev->soc_host.dev, "Limiting master clock to %lu\n", mclk); + dev_warn(dev, "Limiting master clock to %lu\n", mclk); } /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ @@ -856,8 +864,8 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(pcdev->soc_host.dev, "LCD clock %luHz, target freq %luHz, " - "divisor %u\n", lcdclk, mclk, div); + dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n", + lcdclk, mclk, div); return div; } @@ -874,14 +882,15 @@ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev, static void pxa_camera_activate(struct pxa_camera_dev *pcdev) { struct pxacamera_platform_data *pdata = pcdev->pdata; + struct device *dev = pcdev->soc_host.v4l2_dev.dev; u32 cicr4 = 0; - dev_dbg(pcdev->soc_host.dev, "Registered platform device at %p data %p\n", + dev_dbg(dev, "Registered platform device at %p data %p\n", pcdev, pdata); if (pdata && pdata->init) { - dev_dbg(pcdev->soc_host.dev, "%s: Init gpios\n", __func__); - pdata->init(pcdev->soc_host.dev); + dev_dbg(dev, "%s: Init gpios\n", __func__); + pdata->init(dev); } /* disable all interrupts */ @@ -923,7 +932,8 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) struct videobuf_buffer *vb; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->soc_host.dev, "Camera interrupt status 0x%lx\n", status); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "Camera interrupt status 0x%lx\n", status); if (!status) return IRQ_NONE; @@ -955,24 +965,18 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; - int ret; - if (pcdev->icd) { - ret = -EBUSY; - goto ebusy; - } - - dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n", - icd->devnum); + if (pcdev->icd) + return -EBUSY; pxa_camera_activate(pcdev); - ret = icd->ops->init(icd); - if (!ret) - pcdev->icd = icd; + pcdev->icd = icd; -ebusy: - return ret; + dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n", + icd->devnum); + + return 0; } /* Called with .video_lock held */ @@ -983,7 +987,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) BUG_ON(icd != pcdev->icd); - dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n", icd->devnum); /* disable capture, disable interrupts */ @@ -994,8 +998,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) DCSR(pcdev->dma_chans[1]) = 0; DCSR(pcdev->dma_chans[2]) = 0; - icd->ops->release(icd); - pxa_camera_deactivate(pcdev); pcdev->icd = NULL; @@ -1043,57 +1045,17 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, return 0; } -static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +static void pxa_camera_setup_cicr(struct soc_camera_device *icd, + unsigned long flags, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; - unsigned long dw, bpp, bus_flags, camera_flags, common_flags; + unsigned long dw, bpp; u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0; - int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); - - if (ret < 0) - return ret; - - camera_flags = icd->ops->query_bus_param(icd); - - common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); - if (!common_flags) - return -EINVAL; - - pcdev->channels = 1; - - /* Make choises, based on platform preferences */ - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & PXA_CAMERA_HSP) - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & PXA_CAMERA_VSP) - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; - } - - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { - if (pcdev->platform_flags & PXA_CAMERA_PCP) - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; - else - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; - } - - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) - return ret; /* Datawidth is now guaranteed to be equal to one of the three values. * We fix bit-per-pixel equal to data-width... */ - switch (common_flags & SOCAM_DATAWIDTH_MASK) { + switch (flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_10: dw = 4; bpp = 0x40; @@ -1114,18 +1076,18 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) cicr4 |= CICR4_PCLK_EN; if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) cicr4 |= CICR4_MCLK_EN; - if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + if (flags & SOCAM_PCLK_SAMPLE_FALLING) cicr4 |= CICR4_PCP; - if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + if (flags & SOCAM_HSYNC_ACTIVE_LOW) cicr4 |= CICR4_HSP; - if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + if (flags & SOCAM_VSYNC_ACTIVE_LOW) cicr4 |= CICR4_VSP; cicr0 = __raw_readl(pcdev->base + CICR0); if (cicr0 & CICR0_ENB) __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0); - cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw; switch (pixfmt) { case V4L2_PIX_FMT_YUV422P: @@ -1154,7 +1116,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) } cicr2 = 0; - cicr3 = CICR3_LPF_VAL(icd->height - 1) | + cicr3 = CICR3_LPF_VAL(icd->user_height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); cicr4 |= pcdev->mclk_divisor; @@ -1168,6 +1130,59 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)); cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK; __raw_writel(cicr0, pcdev->base + CICR0); +} + +static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned long bus_flags, camera_flags, common_flags; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + struct pxa_cam *cam = icd->host_priv; + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + if (!common_flags) + return -EINVAL; + + pcdev->channels = 1; + + /* Make choises, based on platform preferences */ + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_HSP) + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_VSP) + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (pcdev->platform_flags & PXA_CAMERA_PCP) + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + } + + cam->flags = common_flags; + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + pxa_camera_setup_cicr(icd, common_flags, pixfmt); return 0; } @@ -1231,8 +1246,9 @@ static int required_buswidth(const struct soc_camera_data_format *fmt) static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->dev.parent; int formats = 0, buswidth, ret; + struct pxa_cam *cam; buswidth = required_buswidth(icd->formats + idx); @@ -1243,6 +1259,16 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, if (ret < 0) return 0; + if (!icd->host_priv) { + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + switch (icd->formats[idx].fourcc) { case V4L2_PIX_FMT_UYVY: formats++; @@ -1251,7 +1277,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(dev, "Providing format %s using %s\n", pxa_camera_formats[0].name, icd->formats[idx].name); } @@ -1266,7 +1292,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s packed\n", + dev_dbg(dev, "Providing format %s packed\n", icd->formats[idx].name); } break; @@ -1278,7 +1304,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, + dev_dbg(dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -1287,31 +1313,80 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, return formats; } +static void pxa_camera_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int pxa_camera_check_frame(struct v4l2_pix_format *pix) +{ + /* limit to pxa hardware capabilities */ + return pix->height < 32 || pix->height > 2048 || pix->width < 48 || + pix->width > 2048 || (pix->width & 0x01); +} + static int pxa_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { + struct v4l2_rect *rect = &a->c; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_sense sense = { .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix, pix_tmp; + struct pxa_cam *cam = icd->host_priv; int ret; /* If PCLK is used to latch data from the sensor, check sense */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) icd->sense = &sense; - ret = icd->ops->set_crop(icd, rect); + ret = v4l2_subdev_call(sd, video, s_crop, a); icd->sense = NULL; if (ret < 0) { - dev_warn(ici->dev, "Failed to crop to %ux%u@%u:%u\n", + dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n", rect->width, rect->height, rect->left, rect->top); - } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { + return ret; + } + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + pix_tmp = *pix; + if (pxa_camera_check_frame(pix)) { + /* + * Camera cropping produced a frame beyond our capabilities. + * FIXME: just extract a subframe, that we can process. + */ + v4l_bound_align_image(&pix->width, 48, 2048, 1, + &pix->height, 32, 2048, 0, + icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? + 4 : 0); + ret = v4l2_subdev_call(sd, video, s_fmt, &f); + if (ret < 0) + return ret; + + if (pxa_camera_check_frame(pix)) { + dev_warn(icd->dev.parent, + "Inconsistent state. Use S_FMT to repair\n"); + return -EINVAL; + } + } + + if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(ici->dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1319,6 +1394,11 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, recalculate_fifo_timeout(pcdev, sense.pixel_clock); } + icd->user_width = pix->width; + icd->user_height = pix->height; + + pxa_camera_setup_cicr(icd, cam->flags, icd->current_fmt->fourcc); + return ret; } @@ -1327,6 +1407,8 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_data_format *cam_fmt = NULL; const struct soc_camera_format_xlate *xlate = NULL; struct soc_camera_sense sense = { @@ -1339,7 +1421,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -1350,16 +1432,21 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, icd->sense = &sense; cam_f.fmt.pix.pixelformat = cam_fmt->fourcc; - ret = icd->ops->set_fmt(icd, &cam_f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); icd->sense = NULL; if (ret < 0) { - dev_warn(ici->dev, "Failed to configure for format %x\n", + dev_warn(dev, "Failed to configure for format %x\n", pix->pixelformat); + } else if (pxa_camera_check_frame(pix)) { + dev_warn(dev, + "Camera driver produced an unsupported frame %dx%d\n", + pix->width, pix->height); + ret = -EINVAL; } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(ici->dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1379,6 +1466,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; __u32 pixfmt = pix->pixelformat; @@ -1387,7 +1475,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->v4l2_dev.dev, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1399,7 +1487,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, */ v4l_bound_align_image(&pix->width, 48, 2048, 1, &pix->height, 32, 2048, 0, - xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); + pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); @@ -1408,15 +1496,15 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, /* camera has to see its format, but the user the original one */ pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); - pix->pixelformat = xlate->host_fmt->fourcc; + ret = v4l2_subdev_call(sd, video, try_fmt, f); + pix->pixelformat = pixfmt; field = pix->field; if (field == V4L2_FIELD_ANY) { pix->field = V4L2_FIELD_NONE; } else if (field != V4L2_FIELD_NONE) { - dev_err(&icd->dev, "Field type %d unsupported.\n", field); + dev_err(icd->dev.parent, "Field type %d unsupported.\n", field); return -EINVAL; } @@ -1522,6 +1610,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .resume = pxa_camera_resume, .set_crop = pxa_camera_set_crop, .get_formats = pxa_camera_get_formats, + .put_formats = pxa_camera_put_formats, .set_fmt = pxa_camera_set_fmt, .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, @@ -1579,7 +1668,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->mclk = 20000000; } - pcdev->mclk_divisor = mclk_get_divisor(pcdev); + pcdev->mclk_divisor = mclk_get_divisor(pdev, pcdev); INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); @@ -1644,7 +1733,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; pcdev->soc_host.ops = &pxa_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; - pcdev->soc_host.dev = &pdev->dev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); @@ -1726,3 +1815,4 @@ module_exit(pxa_camera_exit); MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index c25e81af5ce..c3e96f07097 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -40,7 +40,7 @@ /* insmod options */ static unsigned int debug; static unsigned int xtal; -static unsigned int rbds; +static unsigned int mmbs; static unsigned int plvl; static unsigned int bufblocks = 100; @@ -48,8 +48,8 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); module_param(xtal, int, 0); MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); -module_param(rbds, int, 0); -MODULE_PARM_DESC(rbds, "select mode, 0=RDS, 1=RBDS, default 0"); +module_param(mmbs, int, 0); +MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); module_param(plvl, int, 0); MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); module_param(bufblocks, int, 0); @@ -78,6 +78,7 @@ struct saa6588 { unsigned char last_blocknum; wait_queue_head_t read_queue; int data_available_for_read; + u8 sync; }; static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) @@ -261,13 +262,16 @@ static void saa6588_i2c_poll(struct saa6588 *s) unsigned char tmp; /* Although we only need 3 bytes, we have to read at least 6. - SAA6588 returns garbage otherwise */ + SAA6588 returns garbage otherwise. */ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { if (debug > 1) dprintk(PREFIX "read error!\n"); return; } + s->sync = tmpbuf[0] & 0x10; + if (!s->sync) + return; blocknum = tmpbuf[0] >> 5; if (blocknum == s->last_blocknum) { if (debug > 3) @@ -286,9 +290,8 @@ static void saa6588_i2c_poll(struct saa6588 *s) occurred during reception of this block. Bit 6: Corrected bit. Indicates that an error was corrected for this data block. - Bits 5-3: Received Offset. Indicates the offset received - by the sync system. - Bits 2-0: Offset Name. Indicates the offset applied to this data. + Bits 5-3: Same as bits 0-2. + Bits 2-0: Block number. SAA6588 byte order is Status-MSB-LSB, so we have to swap the first and the last of the 3 bytes block. @@ -298,12 +301,21 @@ static void saa6588_i2c_poll(struct saa6588 *s) tmpbuf[2] = tmpbuf[0]; tmpbuf[0] = tmp; + /* Map 'Invalid block E' to 'Invalid Block' */ + if (blocknum == 6) + blocknum = V4L2_RDS_BLOCK_INVALID; + /* And if are not in mmbs mode, then 'Block E' is also mapped + to 'Invalid Block'. As far as I can tell MMBS is discontinued, + and if there is ever a need to support E blocks, then please + contact the linux-media mailinglist. */ + else if (!mmbs && blocknum == 5) + blocknum = V4L2_RDS_BLOCK_INVALID; tmp = blocknum; tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ if ((tmpbuf[2] & 0x03) == 0x03) - tmp |= 0x80; /* uncorrectable error */ + tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ else if ((tmpbuf[2] & 0x03) != 0x00) - tmp |= 0x40; /* corrected error */ + tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ spin_lock_irqsave(&s->lock, flags); @@ -321,14 +333,14 @@ static void saa6588_work(struct work_struct *work) schedule_delayed_work(&s->work, msecs_to_jiffies(20)); } -static int saa6588_configure(struct saa6588 *s) +static void saa6588_configure(struct saa6588 *s) { struct i2c_client *client = v4l2_get_subdevdata(&s->sd); unsigned char buf[3]; int rc; buf[0] = cSyncRestart; - if (rbds) + if (mmbs) buf[0] |= cProcessingModeRBDS; buf[1] = cFlywheelDefault; @@ -374,8 +386,6 @@ static int saa6588_configure(struct saa6588 *s) rc = i2c_master_send(client, buf, 3); if (rc != 3) printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); - - return 0; } /* ---------------------------------------------------------------------- */ @@ -416,6 +426,24 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return 0; } +static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + vt->capability |= V4L2_TUNER_CAP_RDS; + if (s->sync) + vt->rxsubchans |= V4L2_TUNER_SUB_RDS; + return 0; +} + +static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + saa6588_configure(s); + return 0; +} + static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -430,8 +458,14 @@ static const struct v4l2_subdev_core_ops saa6588_core_ops = { .ioctl = saa6588_ioctl, }; +static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { + .g_tuner = saa6588_g_tuner, + .s_tuner = saa6588_s_tuner, +}; + static const struct v4l2_subdev_ops saa6588_ops = { .core = &saa6588_core_ops, + .tuner = &saa6588_tuner_ops, }; /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 5bcce092e80..22bfd62c955 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -47,6 +47,7 @@ config VIDEO_SAA7134_DVB select DVB_TDA10048 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10039 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index 63c4b8f1f54..1eabff6b245 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -468,7 +468,7 @@ static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) return -ERANGE; - new = old; + params->au_encoding = new; break; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: old = params->au_l2_bitrate; diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 8b0b64a8987..d48c450ed77 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -40,6 +40,7 @@ MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); */ /* defaults */ +#define MIXER_ADDR_UNSELECTED -1 #define MIXER_ADDR_TVTUNER 0 #define MIXER_ADDR_LINE1 1 #define MIXER_ADDR_LINE2 2 @@ -68,7 +69,9 @@ typedef struct snd_card_saa7134 { struct snd_card *card; spinlock_t mixer_lock; int mixer_volume[MIXER_ADDR_LAST+1][2]; - int capture_source[MIXER_ADDR_LAST+1][2]; + int capture_source_addr; + int capture_source[2]; + struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1]; struct pci_dev *pci; struct saa7134_dev *dev; @@ -314,6 +317,115 @@ static int dsp_buffer_free(struct saa7134_dev *dev) return 0; } +/* + * Setting the capture source and updating the ALSA controls + */ +static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol, + int left, int right, bool force_notify) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0, addr = kcontrol->private_value; + int active, old_addr; + u32 anabar, xbarin; + int analog_io, rate; + struct saa7134_dev *dev; + + dev = chip->dev; + + spin_lock_irq(&chip->mixer_lock); + + active = left != 0 || right != 0; + old_addr = chip->capture_source_addr; + + /* The active capture source cannot be deactivated */ + if (active) { + change = old_addr != addr || + chip->capture_source[0] != left || + chip->capture_source[1] != right; + + chip->capture_source[0] = left; + chip->capture_source[1] = right; + chip->capture_source_addr = addr; + dev->dmasound.input = addr; + } + spin_unlock_irq(&chip->mixer_lock); + + if (change) { + switch (dev->pci->device) { + + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (addr) { + case MIXER_ADDR_TVTUNER: + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0xc0); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, 0x00); + break; + case MIXER_ADDR_LINE1: + case MIXER_ADDR_LINE2: + analog_io = (MIXER_ADDR_LINE1 == addr) ? + 0x00 : 0x08; + rate = (32000 == dev->dmasound.rate) ? + 0x01 : 0x03; + saa_andorb(SAA7134_ANALOG_IO_SELECT, + 0x08, analog_io); + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0x80); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, rate); + break; + } + + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + xbarin = 0x03; /* adc */ + anabar = 0; + switch (addr) { + case MIXER_ADDR_TVTUNER: + xbarin = 0; /* Demodulator */ + anabar = 2; /* DACs */ + break; + case MIXER_ADDR_LINE1: + anabar = 0; /* aux1, aux1 */ + break; + case MIXER_ADDR_LINE2: + anabar = 9; /* aux2, aux2 */ + break; + } + + /* output xbar always main channel */ + saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, + 0xbbbb10); + + if (left || right) { + /* We've got data, turn the input on */ + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + xbarin); + saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); + } else { + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + 0); + saa_writel(SAA7133_ANALOG_IO_SELECT, 0); + } + break; + } + } + + if (change) { + if (force_notify) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[addr]->id); + + if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[old_addr]->id); + } + + return change; +} /* * ALSA PCM preparation @@ -401,6 +513,10 @@ static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream dev->dmasound.rate = runtime->rate; + /* Setup and update the card/ALSA controls */ + snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1, + true); + return 0; } @@ -435,6 +551,16 @@ snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) /* * ALSA hardware capabilities definition + * + * Report only 32kHz for ALSA: + * + * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz + * only + * - SAA7134 for TV mode uses DemDec mode (32kHz) + * - Radio works in 32kHz only + * - When recording 48kHz from Line1/Line2, switching of capture source to TV + * means + * switching to 32kHz without any frequency translation */ static struct snd_pcm_hardware snd_card_saa7134_capture = @@ -448,9 +574,9 @@ static struct snd_pcm_hardware snd_card_saa7134_capture = SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_U16_LE | \ SNDRV_PCM_FMTBIT_U16_BE, - .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .rates = SNDRV_PCM_RATE_32000, .rate_min = 32000, - .rate_max = 48000, + .rate_max = 32000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (256*1024), @@ -836,8 +962,13 @@ static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, int addr = kcontrol->private_value; spin_lock_irq(&chip->mixer_lock); - ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; - ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; + if (chip->capture_source_addr == addr) { + ucontrol->value.integer.value[0] = chip->capture_source[0]; + ucontrol->value.integer.value[1] = chip->capture_source[1]; + } else { + ucontrol->value.integer.value[0] = 0; + ucontrol->value.integer.value[1] = 0; + } spin_unlock_irq(&chip->mixer_lock); return 0; @@ -846,87 +977,22 @@ static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol) { - snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); - int change, addr = kcontrol->private_value; int left, right; - u32 anabar, xbarin; - int analog_io, rate; - struct saa7134_dev *dev; - - dev = chip->dev; - left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; - spin_lock_irq(&chip->mixer_lock); - - change = chip->capture_source[addr][0] != left || - chip->capture_source[addr][1] != right; - chip->capture_source[addr][0] = left; - chip->capture_source[addr][1] = right; - dev->dmasound.input=addr; - spin_unlock_irq(&chip->mixer_lock); - - - if (change) { - switch (dev->pci->device) { - - case PCI_DEVICE_ID_PHILIPS_SAA7134: - switch (addr) { - case MIXER_ADDR_TVTUNER: - saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); - saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); - break; - case MIXER_ADDR_LINE1: - case MIXER_ADDR_LINE2: - analog_io = (MIXER_ADDR_LINE1 == addr) ? 0x00 : 0x08; - rate = (32000 == dev->dmasound.rate) ? 0x01 : 0x03; - saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io); - saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80); - saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); - break; - } - - break; - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - xbarin = 0x03; // adc - anabar = 0; - switch (addr) { - case MIXER_ADDR_TVTUNER: - xbarin = 0; // Demodulator - anabar = 2; // DACs - break; - case MIXER_ADDR_LINE1: - anabar = 0; // aux1, aux1 - break; - case MIXER_ADDR_LINE2: - anabar = 9; // aux2, aux2 - break; - } - - /* output xbar always main channel */ - saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, 0xbbbb10); - if (left || right) { // We've got data, turn the input on - saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, xbarin); - saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); - } else { - saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, 0); - saa_writel(SAA7133_ANALOG_IO_SELECT, 0); - } - break; - } - } - - return change; + return snd_saa7134_capsrc_set(kcontrol, left, right, false); } -static struct snd_kcontrol_new snd_saa7134_controls[] = { +static struct snd_kcontrol_new snd_saa7134_volume_controls[] = { SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), -SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), -SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2), +}; + +static struct snd_kcontrol_new snd_saa7134_capture_controls[] = { +SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), +SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), }; @@ -941,17 +1007,33 @@ SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) { struct snd_card *card = chip->card; + struct snd_kcontrol *kcontrol; unsigned int idx; - int err; + int err, addr; if (snd_BUG_ON(!chip)) return -EINVAL; strcpy(card->mixername, "SAA7134 Mixer"); - for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_controls); idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_saa7134_controls[idx], chip))) < 0) + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx], + chip); + err = snd_ctl_add(card, kcontrol); + if (err < 0) return err; } + + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx], + chip); + addr = snd_saa7134_capture_controls[idx].private_value; + chip->capture_ctl[addr] = kcontrol; + err = snd_ctl_add(card, kcontrol); + if (err < 0) + return err; + } + + chip->capture_source_addr = MIXER_ADDR_UNSELECTED; return 0; } diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 06861b782b9..71145bff94f 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -32,6 +32,7 @@ #include <media/tveeprom.h> #include "tea5767.h" #include "tda18271.h" +#include "xc5000.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -265,6 +266,56 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x10000, }, }, + [SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = { + /* RoverMedia TV Link Pro FM (LR138 REV:I) */ + /* Eugene Yudin <Eugene.Yudin@gmail.com> */ + .name = "RoverMedia TV Link Pro FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0xe000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x8000, + .tv = 1, + }, { + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x8000, + }, + }, [SAA7134_BOARD_EMPRESS] = { /* "Gert Vervoort" <gert.vervoort@philips.com> */ .name = "EMPRESS", @@ -1364,6 +1415,42 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }, }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_505] = { + /* Vasiliy Temnikov <vaka@newmail.ru> */ + .name = "AverMedia AverTV Studio 505", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, [SAA7134_BOARD_UPMOST_PURPLE_TV] = { .name = "UPMOST PURPLE TV", .audio_clock = 0x00187de7, @@ -1633,7 +1720,7 @@ struct saa7134_board saa7134_boards[] = { }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x00300001, }, .mute = { @@ -3331,8 +3418,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { - .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { + .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -3363,8 +3450,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0800100, /* GPIO 23 HI for FM */ }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { - .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -3663,8 +3750,8 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, .gpio = 0x0200000, }, - }, - [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { + }, + [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { .name = "ASUSTeK P7131 Analog", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, @@ -4077,10 +4164,11 @@ struct saa7134_board saa7134_boards[] = { /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 505 RDS", .audio_clock = 0x00200000, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4141,10 +4229,11 @@ struct saa7134_board saa7134_boards[] = { /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 507 RDS", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4175,6 +4264,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4290,7 +4380,7 @@ struct saa7134_board saa7134_boards[] = { /* Andrey Melnikoff <temnota@kmv.ru> */ .name = "Beholder BeholdTV 607 FM", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -4318,7 +4408,7 @@ struct saa7134_board saa7134_boards[] = { /* Andrey Melnikoff <temnota@kmv.ru> */ .name = "Beholder BeholdTV 609 FM", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -4350,6 +4440,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4378,6 +4469,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4402,10 +4494,11 @@ struct saa7134_board saa7134_boards[] = { /* Andrey Melnikoff <temnota@kmv.ru> */ .name = "Beholder BeholdTV 607 RDS", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4430,10 +4523,11 @@ struct saa7134_board saa7134_boards[] = { /* Andrey Melnikoff <temnota@kmv.ru> */ .name = "Beholder BeholdTV 609 RDS", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4536,10 +4630,11 @@ struct saa7134_board saa7134_boards[] = { /* Alexey Osipov <lion-simba@pridelands.ru> */ .name = "Beholder BeholdTV M6 Extra", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .tuner_type = TUNER_PHILIPS_FM1216MK5, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { @@ -4861,7 +4956,7 @@ struct saa7134_board saa7134_boards[] = { /* Igor Kuznetsov <igk@igk.ru> */ .name = "Beholder BeholdTV H6", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -5116,6 +5211,74 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x00, }, }, + [SAA7134_BOARD_VIDEOMATE_S350] = { + /* Jan D. Louw <jd.louw@mweb.co.za */ + .name = "Compro VideoMate S350/S300", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, /* Not tested */ + .amux = LINE1 + } }, + }, + [SAA7134_BOARD_BEHOLD_X7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV X7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_ZOLID_HYBRID_PCI] = { + .name = "Zolid Hybrid TV Tuner PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + } }, + .radio = { /* untested */ + .name = name_radio, + .amux = TV, + }, + }, + }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5374,6 +5537,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa115, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0x2108, .driver_data = SAA7134_BOARD_AVERMEDIA_305, },{ @@ -5862,31 +6031,31 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6706, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6707, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6708, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6709, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x670a, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -6223,7 +6392,30 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0xf31d, .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, - + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_S350, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7595, + .driver_data = SAA7134_BOARD_BEHOLD_X7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x19d1, /* RoverMedia */ + .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ + .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2004, + .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -6310,6 +6502,32 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, return -EINVAL; } +static int saa7134_xc5000_callback(struct saa7134_dev *dev, + int command, int arg) +{ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_X7: + if (command == XC5000_TUNER_RESET) { + /* Down and UP pheripherial RESET pin for reset all chips */ + saa_writeb(SAA7134_SPECIAL_MODE, 0x00); + msleep(10); + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + msleep(10); + } + break; + default: + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); + saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); + saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); + saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); + saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); + saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, + 0x0001e000, 0x0001e000); + break; + } + return 0; +} static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, int command, int arg) @@ -6363,8 +6581,8 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, switch (command) { case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; default: @@ -6384,8 +6602,8 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, int ret; switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); break; @@ -6406,6 +6624,8 @@ int saa7134_tuner_callback(void *priv, int component, int command, int arg) return saa7134_tda8290_callback(dev, command, arg); case TUNER_XC2028: return saa7134_xc2028_callback(dev, command, arg); + case TUNER_XC5000: + return saa7134_xc5000_callback(dev, command, arg); } } else { printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); @@ -6427,7 +6647,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ - case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ @@ -6435,7 +6655,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ - case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: @@ -6476,6 +6696,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_307: @@ -6500,7 +6721,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -6525,6 +6746,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_REAL_ANGEL_220: case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -6625,8 +6847,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); msleep(1); @@ -6653,6 +6875,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: + case SAA7134_BOARD_BEHOLD_X7: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: @@ -6673,6 +6896,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); break; + case SAA7134_BOARD_VIDEOMATE_S350: + dev->has_remote = SAA7134_REMOTE_GPIO; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + break; } return 0; } @@ -6891,8 +7119,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: hauppauge_eeprom(dev, dev->eedata+0x80); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: @@ -7007,22 +7235,22 @@ int saa7134_board_init2(struct saa7134_dev *dev) if (dev->radio_type != UNSET) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - dev->radio_addr); + dev->radio_addr, NULL); if (has_demod) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (dev->tuner_addr == ADDR_UNSET) { enum v4l2_i2c_tuner_type type = has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(type)); + 0, v4l2_i2c_tuner_addrs(type)); } else { v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - dev->tuner_addr); + dev->tuner_addr, NULL); } } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 94a023a14bb..f87757fccc7 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1000,7 +1000,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, struct v4l2_subdev *sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "saa6752hs", "saa6752hs", - saa7134_boards[dev->board].empress_addr); + saa7134_boards[dev->board].empress_addr, NULL); if (sd) sd->grp_id = GRP_EMPRESS; @@ -1009,11 +1009,13 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (saa7134_boards[dev->board].rds_addr) { struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev_addr(&dev->v4l2_dev, + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "saa6588", "saa6588", - saa7134_boards[dev->board].rds_addr); - if (sd) + 0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr)); + if (sd) { printk(KERN_INFO "%s: found RDS decoder\n", dev->name); + dev->has_rds = 1; + } } request_submodules(dev); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 31930f26ffc..a26e997a9ce 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -56,6 +56,7 @@ #include "zl10353.h" #include "zl10036.h" +#include "zl10039.h" #include "mt312.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); @@ -968,6 +969,10 @@ static struct zl10036_config avertv_a700_tuner = { .tuner_address = 0x60, }; +static struct mt312_config zl10313_compro_s350_config = { + .demod_address = 0x0e, +}; + static struct lgdt3305_config hcw_lgdt3305_config = { .i2c_addr = 0x0e, .mpeg_mode = LGDT3305_MPEG_SERIAL, @@ -1002,12 +1007,29 @@ static struct tda18271_config hcw_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .config = 3, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda829x_config tda829x_no_probe = { .probe_tuner = TDA829X_DONT_PROBE, }; +static struct tda10048_config zolid_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_config zolid_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, +}; + /* ================================================================== * Core code */ @@ -1119,7 +1141,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_2) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: fe0->dvb.frontend = dvb_attach(tda10048_attach, &hcw_tda10048_config, &dev->i2c_adap); @@ -1147,7 +1169,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: fe0->dvb.frontend = dvb_attach(lgdt3305_attach, &hcw_lgdt3305_config, &dev->i2c_adap); @@ -1457,7 +1479,7 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { dvb_attach(simple_tuner_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3); + TUNER_PHILIPS_FMD1216MEX_MK3); } break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: @@ -1473,6 +1495,29 @@ static int dvb_init(struct saa7134_dev *dev) } } break; + case SAA7134_BOARD_VIDEOMATE_S350: + fe0->dvb.frontend = dvb_attach(mt312_attach, + &zl10313_compro_s350_config, &dev->i2c_adap); + if (fe0->dvb.frontend) + if (dvb_attach(zl10039_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap) == NULL) + wprintk("%s: No zl10039 found!\n", + __func__); + + break; + case SAA7134_BOARD_ZOLID_HYBRID_PCI: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &zolid_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &zolid_tda18271_config); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 6e219c2db84..a0e8c62e6ae 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -251,6 +251,10 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (data[10] != 0x6b && data[11] != 0x86 && disable_other_ir) return 0; + /* Wrong data decode fix */ + if (data[9] != (unsigned char)(~data[8])) + return 0; + *ir_key = data[9]; *ir_raw = data[9]; @@ -394,7 +398,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) { struct card_ir *ir; struct input_dev *input_dev; - IR_KEYTAB_TYPE *ir_codes = NULL; + struct ir_scancode_table *ir_codes = NULL; u32 mask_keycode = 0; u32 mask_keydown = 0; u32 mask_keyup = 0; @@ -415,27 +419,28 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYVIDEO3000: case SAA7134_BOARD_FLYTVPLATINUM_FM: case SAA7134_BOARD_FLYTVPLATINUM_MINI2: - ir_codes = ir_codes_flyvideo; + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + ir_codes = &ir_codes_flyvideo_table; mask_keycode = 0xEC00000; mask_keydown = 0x0040000; break; case SAA7134_BOARD_CINERGY400: case SAA7134_BOARD_CINERGY600: case SAA7134_BOARD_CINERGY600_MK3: - ir_codes = ir_codes_cinergy; + ir_codes = &ir_codes_cinergy_table; mask_keycode = 0x00003f; mask_keyup = 0x040000; break; case SAA7134_BOARD_ECS_TVP3XP: case SAA7134_BOARD_ECS_TVP3XP_4CB5: - ir_codes = ir_codes_eztv; + ir_codes = &ir_codes_eztv_table; mask_keycode = 0x00017c; mask_keyup = 0x000002; polling = 50; // ms break; case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVACSSMARTTV: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; mask_keycode = 0x00001F; mask_keyup = 0x000020; polling = 50; // ms @@ -445,13 +450,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_M102: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: - ir_codes = ir_codes_avermedia; + ir_codes = &ir_codes_avermedia_table; mask_keycode = 0x0007C8; mask_keydown = 0x000010; polling = 50; // ms @@ -460,14 +466,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; case SAA7134_BOARD_AVERMEDIA_M135A: - ir_codes = ir_codes_avermedia_m135a; + ir_codes = &ir_codes_avermedia_m135a_table; mask_keydown = 0x0040000; mask_keycode = 0x00013f; nec_gpio = 1; break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - ir_codes = ir_codes_avermedia; + ir_codes = &ir_codes_avermedia_table; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; // ms @@ -476,7 +482,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_AVERMEDIA_A16D: - ir_codes = ir_codes_avermedia_a16d; + ir_codes = &ir_codes_avermedia_a16d_table; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; /* ms */ @@ -485,14 +491,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_KWORLD_TERMINATOR: - ir_codes = ir_codes_pixelview; + ir_codes = &ir_codes_pixelview_table; mask_keycode = 0x00001f; mask_keyup = 0x000060; polling = 50; // ms break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: - ir_codes = ir_codes_manli; + ir_codes = &ir_codes_manli_table; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; /* ms */ @@ -511,25 +517,25 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_BEHOLD_507RDS_MK3: case SAA7134_BOARD_BEHOLD_507RDS_MK5: - ir_codes = ir_codes_manli; + ir_codes = &ir_codes_manli_table; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; /* ms */ break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: - ir_codes = ir_codes_behold_columbus; + ir_codes = &ir_codes_behold_columbus_table; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: - ir_codes = ir_codes_pctv_sedna; + ir_codes = &ir_codes_pctv_sedna_table; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_GOTVIEW_7135: - ir_codes = ir_codes_gotview7135; + ir_codes = &ir_codes_gotview7135_table; mask_keycode = 0x0003CC; mask_keydown = 0x000010; polling = 5; /* ms */ @@ -538,73 +544,78 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: - ir_codes = ir_codes_videomate_tv_pvr; + ir_codes = &ir_codes_videomate_tv_pvr_table; mask_keycode = 0x00003F; mask_keyup = 0x400000; polling = 50; // ms break; case SAA7134_BOARD_PROTEUS_2309: - ir_codes = ir_codes_proteus_2309; + ir_codes = &ir_codes_proteus_2309_table; mask_keycode = 0x00007F; mask_keyup = 0x000080; polling = 50; // ms break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: - ir_codes = ir_codes_videomate_tv_pvr; + ir_codes = &ir_codes_videomate_tv_pvr_table; mask_keycode = 0x003F00; mask_keyup = 0x040000; break; case SAA7134_BOARD_FLYDVBS_LR300: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: - ir_codes = ir_codes_flydvb; + ir_codes = &ir_codes_flydvb_table; mask_keycode = 0x0001F00; mask_keydown = 0x0040000; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: - ir_codes = ir_codes_asus_pc39; + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + ir_codes = &ir_codes_asus_pc39_table; mask_keydown = 0x0040000; rc5_gpio = 1; break; case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: - ir_codes = ir_codes_encore_enltv; + ir_codes = &ir_codes_encore_enltv_table; mask_keycode = 0x00007f; mask_keyup = 0x040000; polling = 50; // ms break; case SAA7134_BOARD_ENCORE_ENLTV_FM53: - ir_codes = ir_codes_encore_enltv_fm53; + ir_codes = &ir_codes_encore_enltv_fm53_table; mask_keydown = 0x0040000; mask_keycode = 0x00007f; nec_gpio = 1; break; case SAA7134_BOARD_10MOONSTVMASTER3: - ir_codes = ir_codes_encore_enltv; + ir_codes = &ir_codes_encore_enltv_table; mask_keycode = 0x5f80000; mask_keyup = 0x8000000; polling = 50; //ms break; case SAA7134_BOARD_GENIUS_TVGO_A11MCE: - ir_codes = ir_codes_genius_tvgo_a11mce; + ir_codes = &ir_codes_genius_tvgo_a11mce_table; mask_keycode = 0xff; mask_keydown = 0xf00000; polling = 50; /* ms */ break; case SAA7134_BOARD_REAL_ANGEL_220: - ir_codes = ir_codes_real_audio_220_32_keys; + ir_codes = &ir_codes_real_audio_220_32_keys_table; mask_keycode = 0x3f00; mask_keyup = 0x4000; polling = 50; /* ms */ break; case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: - ir_codes = ir_codes_kworld_plus_tv_analog; + ir_codes = &ir_codes_kworld_plus_tv_analog_table; mask_keycode = 0x7f; polling = 40; /* ms */ break; + case SAA7134_BOARD_VIDEOMATE_S350: + ir_codes = &ir_codes_videomate_s350_table; + mask_keycode = 0x003f00; + mask_keydown = 0x040000; + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -684,8 +695,6 @@ void saa7134_input_fini(struct saa7134_dev *dev) void saa7134_probe_i2c_ir(struct saa7134_dev *dev) { - struct i2c_board_info info; - struct IR_i2c_init_data init_data; const unsigned short addr_list[] = { 0x7a, 0x47, 0x71, 0x2d, I2C_CLIENT_END @@ -705,32 +714,34 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) return; } - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + memset(&dev->info, 0, sizeof(dev->info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: - init_data.name = "Pinnacle PCTV"; + dev->init_data.name = "Pinnacle PCTV"; if (pinnacle_remote == 0) { - init_data.get_key = get_key_pinnacle_color; - init_data.ir_codes = ir_codes_pinnacle_color; + dev->init_data.get_key = get_key_pinnacle_color; + dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; + dev->info.addr = 0x47; } else { - init_data.get_key = get_key_pinnacle_grey; - init_data.ir_codes = ir_codes_pinnacle_grey; + dev->init_data.get_key = get_key_pinnacle_grey; + dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: - init_data.name = "Purple TV"; - init_data.get_key = get_key_purpletv; - init_data.ir_codes = ir_codes_purpletv; + dev->init_data.name = "Purple TV"; + dev->init_data.get_key = get_key_purpletv; + dev->init_data.ir_codes = &ir_codes_purpletv_table; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: - init_data.name = "MSI TV@nywhere Plus"; - init_data.get_key = get_key_msi_tvanywhere_plus; - init_data.ir_codes = ir_codes_msi_tvanywhere_plus; - info.addr = 0x30; + dev->init_data.name = "MSI TV@nywhere Plus"; + dev->init_data.get_key = get_key_msi_tvanywhere_plus; + dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; + dev->info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from an existing device. Weird... @@ -741,9 +752,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) (1 == rc) ? "yes" : "no"); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - init_data.name = "HVR 1110"; - init_data.get_key = get_key_hvr1110; - init_data.ir_codes = ir_codes_hauppauge_new; + dev->init_data.name = "HVR 1110"; + dev->init_data.get_key = get_key_hvr1110; + dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: case SAA7134_BOARD_BEHOLD_607FM_MK5: @@ -757,26 +768,27 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: - init_data.name = "BeholdTV"; - init_data.get_key = get_key_beholdm6xx; - init_data.ir_codes = ir_codes_behold; + case SAA7134_BOARD_BEHOLD_X7: + dev->init_data.name = "BeholdTV"; + dev->init_data.get_key = get_key_beholdm6xx; + dev->init_data.ir_codes = &ir_codes_behold_table; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - info.addr = 0x40; + dev->info.addr = 0x40; break; } - if (init_data.name) - info.platform_data = &init_data; + if (dev->init_data.name) + dev->info.platform_data = &dev->init_data; /* No need to probe if address is known */ - if (info.addr) { - i2c_new_device(&dev->i2c_adap, &info); + if (dev->info.addr) { + i2c_new_device(&dev->i2c_adap, &dev->info); return; } /* Address not known, fallback to probing */ - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); } static int saa7134_rc5_irq(struct saa7134_dev *dev) diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index ba87128542e..da26f476a30 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1444,7 +1444,6 @@ video_poll(struct file *file, struct poll_table_struct *wait) fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); fh->cap.read_off = 0; } - mutex_unlock(&fh->cap.vb_lock); buf = fh->cap.read_buf; } @@ -1790,7 +1789,7 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) if (0 != err) return err; - if (i < 0 || i >= SAA7134_INPUT_MAX) + if (i >= SAA7134_INPUT_MAX) return -EINVAL; if (NULL == card_in(dev, i).name) return -EINVAL; @@ -1819,6 +1818,8 @@ static int saa7134_querycap(struct file *file, void *priv, V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_TUNER; + if (dev->has_rds) + cap->capabilities |= V4L2_CAP_RDS_CAPTURE; if (saa7134_no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 82268848f26..6ee3e9b7769 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -278,8 +278,8 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 -#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 -#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 +#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 #define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 #define SAA7134_BOARD_BEHOLD_505RDS 159 @@ -292,6 +292,11 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_607RDS_MK5 166 #define SAA7134_BOARD_BEHOLD_609RDS_MK3 167 #define SAA7134_BOARD_BEHOLD_609RDS_MK5 168 +#define SAA7134_BOARD_VIDEOMATE_S350 169 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170 +#define SAA7134_BOARD_BEHOLD_X7 171 +#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 +#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -539,6 +544,7 @@ struct saa7134_dev { struct i2c_adapter i2c_adap; struct i2c_client i2c_client; unsigned char eedata[256]; + int has_rds; /* video overlay */ struct v4l2_framebuffer ovbuf; @@ -584,6 +590,10 @@ struct saa7134_dev { int nosignal; unsigned int insuspend; + /* I2C keyboard data */ + struct i2c_board_info info; + struct IR_i2c_init_data init_data; + /* SAA7134_MPEG_* */ struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; diff --git a/drivers/media/video/saa7164/Kconfig b/drivers/media/video/saa7164/Kconfig new file mode 100644 index 00000000000..35326372517 --- /dev/null +++ b/drivers/media/video/saa7164/Kconfig @@ -0,0 +1,18 @@ +config VIDEO_SAA7164 + tristate "NXP SAA7164 support" + depends on DVB_CORE && PCI && I2C + select I2C_ALGOBIT + select FW_LOADER + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEOBUF_DVB + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + ---help--- + This is a video4linux driver for NXP SAA7164 based + TV cards. + + To compile this driver as a module, choose M here: the + module will be called saa7164 + diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile new file mode 100644 index 00000000000..4b329fd42ad --- /dev/null +++ b/drivers/media/video/saa7164/Makefile @@ -0,0 +1,12 @@ +saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ + saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \ + saa7164-buffer.o + +obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c new file mode 100644 index 00000000000..6f094a96ac8 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-api.c @@ -0,0 +1,600 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/wait.h> + +#include "saa7164.h" + +int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode) +{ + int ret; + + ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR, + SAA_STATE_CONTROL, sizeof(mode), &mode); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); + + return ret; +} + +int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version) +{ + int ret; + + ret = saa7164_cmd_send(dev, 0, GET_CUR, + GET_FW_VERSION_CONTROL, sizeof(u32), version); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); + + return ret; +} + +int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen) +{ + u8 reg[] = { 0x0f, 0x00 }; + + if (buflen < 128) + return -ENOMEM; + + /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */ + /* TODO: Pull the details from the boards struct */ + return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg), + ®[0], 128, buf); +} + + +int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev, + struct saa7164_tsport *port, + tmComResTSFormatDescrHeader_t *tsfmt) +{ + dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex); + dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset); + dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength); + dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength); + dprintk(DBGLVL_API, " bguid = (....)\n"); + + /* Cache the hardware configuration in the port */ + + port->bufcounter = port->hwcfg.BARLocation; + port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); + port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); + port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); + port->bufptr32l = port->hwcfg.BARLocation + + (4 * sizeof(u32)) + + (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); + port->bufptr32h = port->hwcfg.BARLocation + + (4 * sizeof(u32)) + + (sizeof(u32) * port->hwcfg.buffercount); + port->bufptr64 = port->hwcfg.BARLocation + + (4 * sizeof(u32)) + + (sizeof(u32) * port->hwcfg.buffercount); + dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", + port->hwcfg.BARLocation); + + dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n", + port->nr); + + return 0; +} + +int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len) +{ + struct saa7164_tsport *port = 0; + u32 idx, next_offset; + int i; + tmComResDescrHeader_t *hdr, *t; + tmComResExtDevDescrHeader_t *exthdr; + tmComResPathDescrHeader_t *pathhdr; + tmComResAntTermDescrHeader_t *anttermhdr; + tmComResTunerDescrHeader_t *tunerunithdr; + tmComResDMATermDescrHeader_t *vcoutputtermhdr; + tmComResTSFormatDescrHeader_t *tsfmt; + u32 currpath = 0; + + dprintk(DBGLVL_API, + "%s(?,?,%d) sizeof(tmComResDescrHeader_t) = %d bytes\n", + __func__, len, (u32)sizeof(tmComResDescrHeader_t)); + + for (idx = 0; idx < (len - sizeof(tmComResDescrHeader_t)); ) { + + hdr = (tmComResDescrHeader_t *)(buf + idx); + + if (hdr->type != CS_INTERFACE) + return SAA_ERR_NOT_SUPPORTED; + + dprintk(DBGLVL_API, "@ 0x%x = \n", idx); + switch (hdr->subtype) { + case GENERAL_REQUEST: + dprintk(DBGLVL_API, " GENERAL_REQUEST\n"); + break; + case VC_TUNER_PATH: + dprintk(DBGLVL_API, " VC_TUNER_PATH\n"); + pathhdr = (tmComResPathDescrHeader_t *)(buf + idx); + dprintk(DBGLVL_API, " pathid = 0x%x\n", + pathhdr->pathid); + currpath = pathhdr->pathid; + break; + case VC_INPUT_TERMINAL: + dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n"); + anttermhdr = + (tmComResAntTermDescrHeader_t *)(buf + idx); + dprintk(DBGLVL_API, " terminalid = 0x%x\n", + anttermhdr->terminalid); + dprintk(DBGLVL_API, " terminaltype = 0x%x\n", + anttermhdr->terminaltype); + switch (anttermhdr->terminaltype) { + case ITT_ANTENNA: + dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); + break; + case LINE_CONNECTOR: + dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); + break; + case SPDIF_CONNECTOR: + dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); + break; + case COMPOSITE_CONNECTOR: + dprintk(DBGLVL_API, + " = COMPOSITE_CONNECTOR\n"); + break; + case SVIDEO_CONNECTOR: + dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); + break; + case COMPONENT_CONNECTOR: + dprintk(DBGLVL_API, + " = COMPONENT_CONNECTOR\n"); + break; + case STANDARD_DMA: + dprintk(DBGLVL_API, " = STANDARD_DMA\n"); + break; + default: + dprintk(DBGLVL_API, " = undefined (0x%x)\n", + anttermhdr->terminaltype); + } + dprintk(DBGLVL_API, " assocterminal= 0x%x\n", + anttermhdr->assocterminal); + dprintk(DBGLVL_API, " iterminal = 0x%x\n", + anttermhdr->iterminal); + dprintk(DBGLVL_API, " controlsize = 0x%x\n", + anttermhdr->controlsize); + break; + case VC_OUTPUT_TERMINAL: + dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n"); + vcoutputtermhdr = + (tmComResDMATermDescrHeader_t *)(buf + idx); + dprintk(DBGLVL_API, " unitid = 0x%x\n", + vcoutputtermhdr->unitid); + dprintk(DBGLVL_API, " terminaltype = 0x%x\n", + vcoutputtermhdr->terminaltype); + switch (vcoutputtermhdr->terminaltype) { + case ITT_ANTENNA: + dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); + break; + case LINE_CONNECTOR: + dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); + break; + case SPDIF_CONNECTOR: + dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); + break; + case COMPOSITE_CONNECTOR: + dprintk(DBGLVL_API, + " = COMPOSITE_CONNECTOR\n"); + break; + case SVIDEO_CONNECTOR: + dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); + break; + case COMPONENT_CONNECTOR: + dprintk(DBGLVL_API, + " = COMPONENT_CONNECTOR\n"); + break; + case STANDARD_DMA: + dprintk(DBGLVL_API, " = STANDARD_DMA\n"); + break; + default: + dprintk(DBGLVL_API, " = undefined (0x%x)\n", + vcoutputtermhdr->terminaltype); + } + dprintk(DBGLVL_API, " assocterminal= 0x%x\n", + vcoutputtermhdr->assocterminal); + dprintk(DBGLVL_API, " sourceid = 0x%x\n", + vcoutputtermhdr->sourceid); + dprintk(DBGLVL_API, " iterminal = 0x%x\n", + vcoutputtermhdr->iterminal); + dprintk(DBGLVL_API, " BARLocation = 0x%x\n", + vcoutputtermhdr->BARLocation); + dprintk(DBGLVL_API, " flags = 0x%x\n", + vcoutputtermhdr->flags); + dprintk(DBGLVL_API, " interruptid = 0x%x\n", + vcoutputtermhdr->interruptid); + dprintk(DBGLVL_API, " buffercount = 0x%x\n", + vcoutputtermhdr->buffercount); + dprintk(DBGLVL_API, " metadatasize = 0x%x\n", + vcoutputtermhdr->metadatasize); + dprintk(DBGLVL_API, " controlsize = 0x%x\n", + vcoutputtermhdr->controlsize); + dprintk(DBGLVL_API, " numformats = 0x%x\n", + vcoutputtermhdr->numformats); + + t = (tmComResDescrHeader_t *) + ((tmComResDMATermDescrHeader_t *)(buf + idx)); + next_offset = idx + (vcoutputtermhdr->len); + for (i = 0; i < vcoutputtermhdr->numformats; i++) { + t = (tmComResDescrHeader_t *) + (buf + next_offset); + switch (t->subtype) { + case VS_FORMAT_MPEG2TS: + tsfmt = + (tmComResTSFormatDescrHeader_t *)t; + if (currpath == 1) + port = &dev->ts1; + else + port = &dev->ts2; + memcpy(&port->hwcfg, vcoutputtermhdr, + sizeof(*vcoutputtermhdr)); + saa7164_api_configure_port_mpeg2ts(dev, + port, tsfmt); + break; + case VS_FORMAT_MPEG2PS: + dprintk(DBGLVL_API, + " = VS_FORMAT_MPEG2PS\n"); + break; + case VS_FORMAT_VBI: + dprintk(DBGLVL_API, + " = VS_FORMAT_VBI\n"); + break; + case VS_FORMAT_RDS: + dprintk(DBGLVL_API, + " = VS_FORMAT_RDS\n"); + break; + case VS_FORMAT_UNCOMPRESSED: + dprintk(DBGLVL_API, + " = VS_FORMAT_UNCOMPRESSED\n"); + break; + case VS_FORMAT_TYPE: + dprintk(DBGLVL_API, + " = VS_FORMAT_TYPE\n"); + break; + default: + dprintk(DBGLVL_API, + " = undefined (0x%x)\n", + t->subtype); + } + next_offset += t->len; + } + + break; + case TUNER_UNIT: + dprintk(DBGLVL_API, " TUNER_UNIT\n"); + tunerunithdr = + (tmComResTunerDescrHeader_t *)(buf + idx); + dprintk(DBGLVL_API, " unitid = 0x%x\n", + tunerunithdr->unitid); + dprintk(DBGLVL_API, " sourceid = 0x%x\n", + tunerunithdr->sourceid); + dprintk(DBGLVL_API, " iunit = 0x%x\n", + tunerunithdr->iunit); + dprintk(DBGLVL_API, " tuningstandards = 0x%x\n", + tunerunithdr->tuningstandards); + dprintk(DBGLVL_API, " controlsize = 0x%x\n", + tunerunithdr->controlsize); + dprintk(DBGLVL_API, " controls = 0x%x\n", + tunerunithdr->controls); + break; + case VC_SELECTOR_UNIT: + dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n"); + break; + case VC_PROCESSING_UNIT: + dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n"); + break; + case FEATURE_UNIT: + dprintk(DBGLVL_API, " FEATURE_UNIT\n"); + break; + case ENCODER_UNIT: + dprintk(DBGLVL_API, " ENCODER_UNIT\n"); + break; + case EXTENSION_UNIT: + dprintk(DBGLVL_API, " EXTENSION_UNIT\n"); + exthdr = (tmComResExtDevDescrHeader_t *)(buf + idx); + dprintk(DBGLVL_API, " unitid = 0x%x\n", + exthdr->unitid); + dprintk(DBGLVL_API, " deviceid = 0x%x\n", + exthdr->deviceid); + dprintk(DBGLVL_API, " devicetype = 0x%x\n", + exthdr->devicetype); + if (exthdr->devicetype & 0x1) + dprintk(DBGLVL_API, " = Decoder Device\n"); + if (exthdr->devicetype & 0x2) + dprintk(DBGLVL_API, " = GPIO Source\n"); + if (exthdr->devicetype & 0x4) + dprintk(DBGLVL_API, " = Video Decoder\n"); + if (exthdr->devicetype & 0x8) + dprintk(DBGLVL_API, " = Audio Decoder\n"); + if (exthdr->devicetype & 0x20) + dprintk(DBGLVL_API, " = Crossbar\n"); + if (exthdr->devicetype & 0x40) + dprintk(DBGLVL_API, " = Tuner\n"); + if (exthdr->devicetype & 0x80) + dprintk(DBGLVL_API, " = IF PLL\n"); + if (exthdr->devicetype & 0x100) + dprintk(DBGLVL_API, " = Demodulator\n"); + if (exthdr->devicetype & 0x200) + dprintk(DBGLVL_API, " = RDS Decoder\n"); + if (exthdr->devicetype & 0x400) + dprintk(DBGLVL_API, " = Encoder\n"); + if (exthdr->devicetype & 0x800) + dprintk(DBGLVL_API, " = IR Decoder\n"); + if (exthdr->devicetype & 0x1000) + dprintk(DBGLVL_API, " = EEPROM\n"); + if (exthdr->devicetype & 0x2000) + dprintk(DBGLVL_API, + " = VBI Decoder\n"); + if (exthdr->devicetype & 0x10000) + dprintk(DBGLVL_API, + " = Streaming Device\n"); + if (exthdr->devicetype & 0x20000) + dprintk(DBGLVL_API, + " = DRM Device\n"); + if (exthdr->devicetype & 0x40000000) + dprintk(DBGLVL_API, + " = Generic Device\n"); + if (exthdr->devicetype & 0x80000000) + dprintk(DBGLVL_API, + " = Config Space Device\n"); + dprintk(DBGLVL_API, " numgpiopins = 0x%x\n", + exthdr->numgpiopins); + dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n", + exthdr->numgpiogroups); + dprintk(DBGLVL_API, " controlsize = 0x%x\n", + exthdr->controlsize); + break; + case PVC_INFRARED_UNIT: + dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n"); + break; + case DRM_UNIT: + dprintk(DBGLVL_API, " DRM_UNIT\n"); + break; + default: + dprintk(DBGLVL_API, "default %d\n", hdr->subtype); + } + + dprintk(DBGLVL_API, " 1.%x\n", hdr->len); + dprintk(DBGLVL_API, " 2.%x\n", hdr->type); + dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype); + dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid); + + idx += hdr->len; + } + + return 0; +} + +int saa7164_api_enum_subdevs(struct saa7164_dev *dev) +{ + int ret; + u32 buflen = 0; + u8 *buf; + + dprintk(DBGLVL_API, "%s()\n", __func__); + + /* Get the total descriptor length */ + ret = saa7164_cmd_send(dev, 0, GET_LEN, + GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); + + dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n", + __func__, buflen); + + /* Allocate enough storage for all of the descs */ + buf = kzalloc(buflen, GFP_KERNEL); + if (buf == NULL) + return SAA_ERR_NO_RESOURCES; + + /* Retrieve them */ + ret = saa7164_cmd_send(dev, 0, GET_CUR, + GET_DESCRIPTORS_CONTROL, buflen, buf); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); + goto out; + } + + if (saa_debug & DBGLVL_API) + saa7164_dumphex16(dev, buf, (buflen/16)*16); + + saa7164_api_dump_subdevs(dev, buf, buflen); + +out: + kfree(buf); + return ret; +} + +int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, + u32 datalen, u8 *data) +{ + struct saa7164_dev *dev = bus->dev; + u16 len = 0; + int unitid; + u32 regval; + u8 buf[256]; + int ret; + + dprintk(DBGLVL_API, "%s()\n", __func__); + + if (reglen > 4) + return -EIO; + + if (reglen == 1) + regval = *(reg); + else + if (reglen == 2) + regval = ((*(reg) << 8) || *(reg+1)); + else + if (reglen == 3) + regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2)); + else + if (reglen == 4) + regval = ((*(reg) << 24) | (*(reg+1) << 16) | + (*(reg+2) << 8) | *(reg+3)); + + /* Prepare the send buffer */ + /* Bytes 00-03 source register length + * 04-07 source bytes to read + * 08... register address + */ + memset(buf, 0, sizeof(buf)); + memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen); + *((u32 *)(buf + 0 * sizeof(u32))) = reglen; + *((u32 *)(buf + 1 * sizeof(u32))) = datalen; + + unitid = saa7164_i2caddr_to_unitid(bus, addr); + if (unitid < 0) { + printk(KERN_ERR + "%s() error, cannot translate regaddr 0x%x to unitid\n", + __func__, addr); + return -EIO; + } + + ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, + EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); + return -EIO; + } + + dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); + + if (saa_debug & DBGLVL_I2C) + saa7164_dumphex16(dev, buf, 2 * 16); + + ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR, + EXU_REGISTER_ACCESS_CONTROL, len, &buf); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); + else { + if (saa_debug & DBGLVL_I2C) + saa7164_dumphex16(dev, buf, sizeof(buf)); + memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen); + } + + return ret == SAA_OK ? 0 : -EIO; +} + +/* For a given 8 bit i2c address device, write the buffer */ +int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, + u8 *data) +{ + struct saa7164_dev *dev = bus->dev; + u16 len = 0; + int unitid; + int reglen; + u8 buf[256]; + int ret; + + dprintk(DBGLVL_API, "%s()\n", __func__); + + if ((datalen == 0) || (datalen > 232)) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + unitid = saa7164_i2caddr_to_unitid(bus, addr); + if (unitid < 0) { + printk(KERN_ERR + "%s() error, cannot translate regaddr 0x%x to unitid\n", + __func__, addr); + return -EIO; + } + + reglen = saa7164_i2caddr_to_reglen(bus, addr); + if (unitid < 0) { + printk(KERN_ERR + "%s() error, cannot translate regaddr to reglen\n", + __func__); + return -EIO; + } + + ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, + EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); + return -EIO; + } + + dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); + + /* Prepare the send buffer */ + /* Bytes 00-03 dest register length + * 04-07 dest bytes to write + * 08... register address + */ + *((u32 *)(buf + 0 * sizeof(u32))) = reglen; + *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen; + memcpy((buf + 2 * sizeof(u32)), data, datalen); + + if (saa_debug & DBGLVL_I2C) + saa7164_dumphex16(dev, buf, sizeof(buf)); + + ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR, + EXU_REGISTER_ACCESS_CONTROL, len, &buf); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); + + return ret == SAA_OK ? 0 : -EIO; +} + + +int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid, + u8 pin, u8 state) +{ + int ret; + tmComResGPIO_t t; + + dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n", + __func__, unitid, pin, state); + + if ((pin > 7) || (state > 2)) + return SAA_ERR_BAD_PARAMETER; + + t.pin = pin; + t.state = state; + + ret = saa7164_cmd_send(dev, unitid, SET_CUR, + EXU_GPIO_CONTROL, sizeof(t), &t); + if (ret != SAA_OK) + printk(KERN_ERR "%s() error, ret = 0x%x\n", + __func__, ret); + + return ret; +} + +int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, + u8 pin) +{ + return saa7164_api_modify_gpio(dev, unitid, pin, 1); +} + +int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, + u8 pin) +{ + return saa7164_api_modify_gpio(dev, unitid, pin, 0); +} + + + diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c new file mode 100644 index 00000000000..9ca5c83d165 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-buffer.c @@ -0,0 +1,155 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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 "saa7164.h" + +/* The PCI address space for buffer handling looks like this: + + +-u32 wide-------------+ + | + + +-u64 wide------------------------------------+ + + + + +----------------------+ + | CurrentBufferPtr + Pointer to current PCI buffer >-+ + +----------------------+ | + | Unused + | + +----------------------+ | + | Pitch + = 188 (bytes) | + +----------------------+ | + | PCI buffer size + = pitch * number of lines (312) | + +----------------------+ | + |0| Buf0 Write Offset + | + +----------------------+ v + |1| Buf1 Write Offset + | + +----------------------+ | + |2| Buf2 Write Offset + | + +----------------------+ | + |3| Buf3 Write Offset + | + +----------------------+ | + ... More write offsets | + +---------------------------------------------+ | + +0| set of ptrs to PCI pagetables + | + +---------------------------------------------+ | + +1| set of ptrs to PCI pagetables + <--------+ + +---------------------------------------------+ + +2| set of ptrs to PCI pagetables + + +---------------------------------------------+ + +3| set of ptrs to PCI pagetables + >--+ + +---------------------------------------------+ | + ... More buffer pointers | +----------------+ + +->| pt[0] TS data | + | +----------------+ + | + | +----------------+ + +->| pt[1] TS data | + | +----------------+ + | etc + */ + +/* Allocate a new buffer structure and associated PCI space in bytes. + * len must be a multiple of sizeof(u64) + */ +struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port, + u32 len) +{ + struct saa7164_buffer *buf = 0; + struct saa7164_dev *dev = port->dev; + int i; + + if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { + log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); + goto ret; + } + + buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); + if (buf == NULL) { + log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); + goto ret; + } + + buf->port = port; + buf->flags = SAA7164_BUFFER_FREE; + /* TODO: arg len is being ignored */ + buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; + buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; + + /* Allocate contiguous memory */ + buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, + &buf->dma); + if (!buf->cpu) + goto fail1; + + buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, + &buf->pt_dma); + if (!buf->pt_cpu) + goto fail2; + + /* init the buffers to a known pattern, easier during debugging */ + memset(buf->cpu, 0xff, buf->pci_size); + memset(buf->pt_cpu, 0xff, buf->pt_size); + + dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p\n", __func__, buf); + dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", + buf->cpu, (long)buf->dma, buf->pci_size); + dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", + buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); + + /* Format the Page Table Entries to point into the data buffer */ + for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { + + *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ + + } + + goto ret; + +fail2: + pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); +fail1: + kfree(buf); + + buf = 0; +ret: + return buf; +} + +int saa7164_buffer_dealloc(struct saa7164_tsport *port, + struct saa7164_buffer *buf) +{ + struct saa7164_dev *dev = port->dev; + + if ((buf == 0) || (port == 0)) + return SAA_ERR_BAD_PARAMETER; + + dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", __func__, buf); + + if (buf->flags != SAA7164_BUFFER_FREE) + log_warn(" freeing a non-free buffer\n"); + + pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); + pci_free_consistent(port->dev->pci, buf->pt_size, buf->pt_cpu, + buf->pt_dma); + + kfree(buf); + + return SAA_OK; +} + diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c new file mode 100644 index 00000000000..83a04640a25 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-bus.c @@ -0,0 +1,448 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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 "saa7164.h" + +/* The message bus to/from the firmware is a ring buffer in PCI address + * space. Establish the defaults. + */ +int saa7164_bus_setup(struct saa7164_dev *dev) +{ + tmComResBusInfo_t *b = &dev->bus; + + mutex_init(&b->lock); + + b->Type = TYPE_BUS_PCIe; + b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE; + + b->m_pdwSetRing = (u8 *)(dev->bmmio + + ((u32)dev->busdesc.CommandRing)); + + b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE; + + b->m_pdwGetRing = (u8 *)(dev->bmmio + + ((u32)dev->busdesc.ResponseRing)); + + b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE; + + b->m_pdwSetWritePos = (u32 *)((u8 *)(dev->bmmio + + ((u32)dev->intfdesc.BARLocation) + (2 * sizeof(u64)))); + + b->m_pdwSetReadPos = (u32 *)((u8 *)b->m_pdwSetWritePos + + 1 * sizeof(u32)); + + b->m_pdwGetWritePos = (u32 *)((u8 *)b->m_pdwSetWritePos + + 2 * sizeof(u32)); + + b->m_pdwGetReadPos = (u32 *)((u8 *)b->m_pdwSetWritePos + + 3 * sizeof(u32)); + + return 0; +} + +void saa7164_bus_dump(struct saa7164_dev *dev) +{ + tmComResBusInfo_t *b = &dev->bus; + + dprintk(DBGLVL_BUS, "Dumping the bus structure:\n"); + dprintk(DBGLVL_BUS, " .type = %d\n", b->Type); + dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio); + dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize); + dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing); + dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing); + dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing); + dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing); + + dprintk(DBGLVL_BUS, " .m_pdwSetWritePos = 0x%p (0x%08x)\n", + b->m_pdwSetWritePos, *b->m_pdwSetWritePos); + + dprintk(DBGLVL_BUS, " .m_pdwSetReadPos = 0x%p (0x%08x)\n", + b->m_pdwSetReadPos, *b->m_pdwSetReadPos); + + dprintk(DBGLVL_BUS, " .m_pdwGetWritePos = 0x%p (0x%08x)\n", + b->m_pdwGetWritePos, *b->m_pdwGetWritePos); + + dprintk(DBGLVL_BUS, " .m_pdwGetReadPos = 0x%p (0x%08x)\n", + b->m_pdwGetReadPos, *b->m_pdwGetReadPos); +} + +void saa7164_bus_dumpmsg(struct saa7164_dev *dev, tmComResInfo_t* m, void *buf) +{ + dprintk(DBGLVL_BUS, "Dumping msg structure:\n"); + dprintk(DBGLVL_BUS, " .id = %d\n", m->id); + dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags); + dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size); + dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command); + dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector); + dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno); + if (buf) + dprintk(DBGLVL_BUS, " .buffer (ignored)\n"); +} + +/* + * Places a command or a response on the bus. The implementation does not + * know if it is a command or a response it just places the data on the + * bus depending on the bus information given in the tmComResBusInfo_t + * structure. If the command or response does not fit into the bus ring + * buffer it will be refused. + * + * Return Value: + * SAA_OK The function executed successfully. + * < 0 One or more members are not initialized. + */ +int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf) +{ + tmComResBusInfo_t *bus = &dev->bus; + u32 bytes_to_write, read_distance, timeout, curr_srp, curr_swp; + u32 new_swp, space_rem; + int ret = SAA_ERR_BAD_PARAMETER; + + if (!msg) { + printk(KERN_ERR "%s() !msg\n", __func__); + return SAA_ERR_BAD_PARAMETER; + } + + dprintk(DBGLVL_BUS, "%s()\n", __func__); + + msg->size = cpu_to_le16(msg->size); + msg->command = cpu_to_le16(msg->command); + msg->controlselector = cpu_to_le16(msg->controlselector); + + if (msg->size > dev->bus.m_wMaxReqSize) { + printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", + __func__); + return SAA_ERR_BAD_PARAMETER; + } + + if ((msg->size > 0) && (buf == 0)) { + printk(KERN_ERR "%s() Missing message buffer\n", __func__); + return SAA_ERR_BAD_PARAMETER; + } + + /* Lock the bus from any other access */ + mutex_lock(&bus->lock); + + bytes_to_write = sizeof(*msg) + msg->size; + read_distance = 0; + timeout = SAA_BUS_TIMEOUT; + curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos); + curr_swp = le32_to_cpu(*bus->m_pdwSetWritePos); + + /* Deal with ring wrapping issues */ + if (curr_srp > curr_swp) + /* The ring has not wrapped yet */ + read_distance = curr_srp - curr_swp; + else + /* Deal with the wrapped ring */ + read_distance = (curr_srp + bus->m_dwSizeSetRing) - curr_swp; + + dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__, + bytes_to_write); + + dprintk(DBGLVL_BUS, "%s() read_distance = %d\n", __func__, + read_distance); + + dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp); + dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp); + + /* Process the msg and write the content onto the bus */ + while (bytes_to_write >= read_distance) { + + if (timeout-- == 0) { + printk(KERN_ERR "%s() bus timeout\n", __func__); + ret = SAA_ERR_NO_RESOURCES; + goto out; + } + + /* TODO: Review this delay, efficient? */ + /* Wait, allowing the hardware fetch time */ + mdelay(1); + + /* Check the space usage again */ + curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos); + + /* Deal with ring wrapping issues */ + if (curr_srp > curr_swp) + /* Read didn't wrap around the buffer */ + read_distance = curr_srp - curr_swp; + else + /* Deal with the wrapped ring */ + read_distance = (curr_srp + bus->m_dwSizeSetRing) - + curr_swp; + + } + + /* Calculate the new write position */ + new_swp = curr_swp + bytes_to_write; + + dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); + dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__, + bus->m_dwSizeSetRing); + + /* Mental Note: line 462 tmmhComResBusPCIe.cpp */ + + /* Check if we're going to wrap again */ + if (new_swp > bus->m_dwSizeSetRing) { + + /* Ring wraps */ + new_swp -= bus->m_dwSizeSetRing; + + space_rem = bus->m_dwSizeSetRing - curr_swp; + + dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__, + space_rem); + + dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__, + (u32)sizeof(*msg)); + + if (space_rem < sizeof(*msg)) { + dprintk(DBGLVL_BUS, "%s() tr4\n", __func__); + + /* Split the msg into pieces as the ring wraps */ + memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem); + memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem, + sizeof(*msg) - space_rem); + + memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem, + buf, msg->size); + + } else if (space_rem == sizeof(*msg)) { + dprintk(DBGLVL_BUS, "%s() tr5\n", __func__); + + /* Additional data at the beginning of the ring */ + memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); + memcpy(bus->m_pdwSetRing, buf, msg->size); + + } else { + /* Additional data wraps around the ring */ + memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); + if (msg->size > 0) { + memcpy(bus->m_pdwSetRing + curr_swp + + sizeof(*msg), buf, space_rem - + sizeof(*msg)); + memcpy(bus->m_pdwSetRing, (u8 *)buf + + space_rem - sizeof(*msg), + bytes_to_write - space_rem); + } + + } + + } /* (new_swp > bus->m_dwSizeSetRing) */ + else { + dprintk(DBGLVL_BUS, "%s() tr6\n", __func__); + + /* The ring buffer doesn't wrap, two simple copies */ + memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); + memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf, + msg->size); + } + + dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); + + /* TODO: Convert all of the direct PCI writes into + * saa7164_writel/b calls for consistency. + */ + + /* Update the bus write position */ + *bus->m_pdwSetWritePos = cpu_to_le32(new_swp); + ret = SAA_OK; + +out: + mutex_unlock(&bus->lock); + return ret; +} + +/* + * Receive a command or a response from the bus. The implementation does not + * know if it is a command or a response it simply dequeues the data, + * depending on the bus information given in the tmComResBusInfo_t structure. + * + * Return Value: + * 0 The function executed successfully. + * < 0 One or more members are not initialized. + */ +int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf, + int peekonly) +{ + tmComResBusInfo_t *bus = &dev->bus; + u32 bytes_to_read, write_distance, curr_grp, curr_gwp, + new_grp, buf_size, space_rem; + tmComResInfo_t msg_tmp; + int ret = SAA_ERR_BAD_PARAMETER; + + if (msg == 0) + return ret; + + if (msg->size > dev->bus.m_wMaxReqSize) { + printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", + __func__); + return ret; + } + + if ((peekonly == 0) && (msg->size > 0) && (buf == 0)) { + printk(KERN_ERR + "%s() Missing msg buf, size should be %d bytes\n", + __func__, msg->size); + return ret; + } + + mutex_lock(&bus->lock); + + /* Peek the bus to see if a msg exists, if it's not what we're expecting + * then return cleanly else read the message from the bus. + */ + curr_gwp = le32_to_cpu(*bus->m_pdwGetWritePos); + curr_grp = le32_to_cpu(*bus->m_pdwGetReadPos); + + if (curr_gwp == curr_grp) { + dprintk(DBGLVL_BUS, "%s() No message on the bus\n", __func__); + ret = SAA_ERR_EMPTY; + goto out; + } + + bytes_to_read = sizeof(*msg); + + /* Calculate write distance to current read position */ + write_distance = 0; + if (curr_gwp >= curr_grp) + /* Write doesn't wrap around the ring */ + write_distance = curr_gwp - curr_grp; + else + /* Write wraps around the ring */ + write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; + + if (bytes_to_read > write_distance) { + printk(KERN_ERR "%s() No message/response found\n", __func__); + ret = SAA_ERR_INVALID_COMMAND; + goto out; + } + + /* Calculate the new read position */ + new_grp = curr_grp + bytes_to_read; + if (new_grp > bus->m_dwSizeGetRing) { + + /* Ring wraps */ + new_grp -= bus->m_dwSizeGetRing; + space_rem = bus->m_dwSizeGetRing - curr_grp; + + memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem); + memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing, + bytes_to_read - space_rem); + + } else { + /* No wrapping */ + memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read); + } + + /* No need to update the read positions, because this was a peek */ + /* If the caller specifically want to peek, return */ + if (peekonly) { + memcpy(msg, &msg_tmp, sizeof(*msg)); + goto peekout; + } + + /* Check if the command/response matches what is expected */ + if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) || + (msg_tmp.controlselector != msg->controlselector) || + (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) { + + printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); + saa7164_bus_dumpmsg(dev, msg, buf); + saa7164_bus_dumpmsg(dev, &msg_tmp, 0); + ret = SAA_ERR_INVALID_COMMAND; + goto out; + } + + /* Get the actual command and response from the bus */ + buf_size = msg->size; + + bytes_to_read = sizeof(*msg) + msg->size; + /* Calculate write distance to current read position */ + write_distance = 0; + if (curr_gwp >= curr_grp) + /* Write doesn't wrap around the ring */ + write_distance = curr_gwp - curr_grp; + else + /* Write wraps around the ring */ + write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; + + if (bytes_to_read > write_distance) { + printk(KERN_ERR "%s() Invalid bus state, missing msg " + "or mangled ring, faulty H/W / bad code?\n", __func__); + ret = SAA_ERR_INVALID_COMMAND; + goto out; + } + + /* Calculate the new read position */ + new_grp = curr_grp + bytes_to_read; + if (new_grp > bus->m_dwSizeGetRing) { + + /* Ring wraps */ + new_grp -= bus->m_dwSizeGetRing; + space_rem = bus->m_dwSizeGetRing - curr_grp; + + if (space_rem < sizeof(*msg)) { + /* msg wraps around the ring */ + memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem); + memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing, + sizeof(*msg) - space_rem); + if (buf) + memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) - + space_rem, buf_size); + + } else if (space_rem == sizeof(*msg)) { + memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); + if (buf) + memcpy(buf, bus->m_pdwGetRing, buf_size); + } else { + /* Additional data wraps around the ring */ + memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); + if (buf) { + memcpy(buf, bus->m_pdwGetRing + curr_grp + + sizeof(*msg), space_rem - sizeof(*msg)); + memcpy(buf + space_rem - sizeof(*msg), + bus->m_pdwGetRing, bytes_to_read - + space_rem); + } + + } + + } else { + /* No wrapping */ + memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); + if (buf) + memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), + buf_size); + } + + /* Update the read positions, adjusting the ring */ + *bus->m_pdwGetReadPos = cpu_to_le32(new_grp); + +peekout: + msg->size = le16_to_cpu(msg->size); + msg->command = le16_to_cpu(msg->command); + msg->controlselector = le16_to_cpu(msg->controlselector); + ret = SAA_OK; +out: + mutex_unlock(&bus->lock); + return ret; +} + diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c new file mode 100644 index 00000000000..a3c299405f4 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cards.c @@ -0,0 +1,624 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> + +#include "saa7164.h" + +/* The Bridge API needs to understand register widths (in bytes) for the + * attached I2C devices, so we can simplify the virtual i2c mechansms + * and keep the -i2c.c implementation clean. + */ +#define REGLEN_8bit 1 +#define REGLEN_16bit 2 + +struct saa7164_board saa7164_boards[] = { + [SAA7164_BOARD_UNKNOWN] = { + /* Bridge will not load any firmware, without knowing + * the rev this would be fatal. */ + .name = "Unknown", + }, + [SAA7164_BOARD_UNKNOWN_REV2] = { + /* Bridge will load the v2 f/w and dump descriptors */ + /* Required during new board bringup */ + .name = "Generic Rev2", + .chiprev = SAA7164_CHIP_REV2, + }, + [SAA7164_BOARD_UNKNOWN_REV3] = { + /* Bridge will load the v2 f/w and dump descriptors */ + /* Required during new board bringup */ + .name = "Generic Rev3", + .chiprev = SAA7164_CHIP_REV3, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2200] = { + .name = "Hauppauge WinTV-HVR2200", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x1d, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1b, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1e, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x10 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1f, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x12 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = { + .name = "Hauppauge WinTV-HVR2200", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV2, + .unit = {{ + .id = 0x06, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x05, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x10 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1e, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1f, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x12 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = { + .name = "Hauppauge WinTV-HVR2200", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV2, + .unit = {{ + .id = 0x1d, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x05, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1b, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1c, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1e, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x10 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1f, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x12 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2250] = { + .name = "Hauppauge WinTV-HVR2250", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x22, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x07, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x08, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1e, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x20, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x23, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = { + .name = "Hauppauge WinTV-HVR2250", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x28, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x07, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x08, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x24, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x26, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x29, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2250_3] = { + .name = "Hauppauge WinTV-HVR2250", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x26, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x07, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x08, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-1 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x22, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x24, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (TOP)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x32 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x27, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "CX24228/S5H1411-2 (QAM)", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x34 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, +}; +const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); + +/* ------------------------------------------------------------------ */ +/* PCI subsystem IDs */ + +struct saa7164_subid saa7164_subids[] = { + { + .subvendor = 0x0070, + .subdevice = 0x8880, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, + }, { + .subvendor = 0x0070, + .subdevice = 0x8810, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, + }, { + .subvendor = 0x0070, + .subdevice = 0x8980, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2200, + }, { + .subvendor = 0x0070, + .subdevice = 0x8900, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8901, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_3, + }, { + .subvendor = 0x0070, + .subdevice = 0x88A1, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_3, + }, { + .subvendor = 0x0070, + .subdevice = 0x8891, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8851, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, + }, +}; +const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); + +void saa7164_card_list(struct saa7164_dev *dev) +{ + int i; + + if (0 == dev->pci->subsystem_vendor && + 0 == dev->pci->subsystem_device) { + printk(KERN_ERR + "%s: Board has no valid PCIe Subsystem ID and can't\n" + "%s: be autodetected. Pass card=<n> insmod option to\n" + "%s: workaround that. Send complaints to the vendor\n" + "%s: of the TV card. Best regards,\n" + "%s: -- tux\n", + dev->name, dev->name, dev->name, dev->name, dev->name); + } else { + printk(KERN_ERR + "%s: Your board isn't known (yet) to the driver.\n" + "%s: Try to pick one of the existing card configs via\n" + "%s: card=<n> insmod option. Updating to the latest\n" + "%s: version might help as well.\n", + dev->name, dev->name, dev->name, dev->name); + } + + printk(KERN_ERR "%s: Here are valid choices for the card=<n> insmod " + "option:\n", dev->name); + + for (i = 0; i < saa7164_bcount; i++) + printk(KERN_ERR "%s: card=%d -> %s\n", + dev->name, i, saa7164_boards[i].name); +} + +/* TODO: clean this define up into the -cards.c structs */ +#define PCIEBRIDGE_UNITID 2 + +void saa7164_gpio_setup(struct saa7164_dev *dev) +{ + + + switch (dev->board) { + case SAA7164_BOARD_HAUPPAUGE_HVR2200: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2250: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + /* + GPIO 2: s5h1411 / tda10048-1 demod reset + GPIO 3: s5h1411 / tda10048-2 demod reset + GPIO 7: IRBlaster Zilog reset + */ + + /* Reset parts by going in and out of reset */ + saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); + saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); + + msleep(10); + + saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2); + saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3); + break; + } + +} + +static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + /* TODO: Assumption: eeprom on bus 0 */ + tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, + eeprom_data); + + /* Make sure we support the board model */ + switch (tv.model) { + case 88001: + /* Development board - Limit circulation */ + /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) + * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ + case 88021: + /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) + * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */ + break; + case 88041: + /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) + * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ + break; + case 88061: + /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) + * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */ + break; + case 89519: + case 89609: + /* WinTV-HVR2200 (PCIe, Retail, full-height) + * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ + break; + case 89619: + /* WinTV-HVR2200 (PCIe, Retail, half-height) + * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ + break; + default: + printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", + dev->name, tv.model); + break; + } + + printk(KERN_INFO "%s: Hauppauge eeprom: model=%d\n", dev->name, + tv.model); +} + +void saa7164_card_setup(struct saa7164_dev *dev) +{ + static u8 eeprom[256]; + + if (dev->i2c_bus[0].i2c_rc == 0) { + if (saa7164_api_read_eeprom(dev, &eeprom[0], + sizeof(eeprom)) < 0) + return; + } + + switch (dev->board) { + case SAA7164_BOARD_HAUPPAUGE_HVR2200: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2250: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + hauppauge_eeprom(dev, &eeprom[0]); + break; + } +} + +/* With most other drivers, the kernel expects to communicate with subdrivers + * through i2c. This bridge does not allow that, it does not expose any direct + * access to I2C. Instead we have to communicate through the device f/w for + * register access to 'processing units'. Each unit has a unique + * id, regardless of how the physical implementation occurs across + * the three physical i2c busses. The being said if we want leverge of + * the existing kernel drivers for tuners and demods we have to 'speak i2c', + * to this bridge implements 3 virtual i2c buses. This is a helper function + * for those. + * + * Description: Translate the kernels notion of an i2c address and bus into + * the appropriate unitid. + */ +int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr) +{ + /* For a given bus and i2c device address, return the saa7164 unique + * unitid. < 0 on error */ + + struct saa7164_dev *dev = bus->dev; + struct saa7164_unit *unit; + int i; + + for (i = 0; i < SAA7164_MAX_UNITS; i++) { + unit = &saa7164_boards[dev->board].unit[i]; + + if (unit->type == SAA7164_UNIT_UNDEFINED) + continue; + if ((bus->nr == unit->i2c_bus_nr) && + (addr == unit->i2c_bus_addr)) + return unit->id; + } + + return -1; +} + +/* The 7164 API needs to know the i2c register length in advance. + * this is a helper function. Based on a specific chip addr and bus return the + * reg length. + */ +int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr) +{ + /* For a given bus and i2c device address, return the + * saa7164 registry address width. < 0 on error + */ + + struct saa7164_dev *dev = bus->dev; + struct saa7164_unit *unit; + int i; + + for (i = 0; i < SAA7164_MAX_UNITS; i++) { + unit = &saa7164_boards[dev->board].unit[i]; + + if (unit->type == SAA7164_UNIT_UNDEFINED) + continue; + + if ((bus->nr == unit->i2c_bus_nr) && + (addr == unit->i2c_bus_addr)) + return unit->i2c_reg_len; + } + + return -1; +} +/* TODO: implement a 'findeeprom' functio like the above and fix any other + * eeprom related todo's in -api.c. + */ + +/* Translate a unitid into a x readable device name, for display purposes. */ +char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid) +{ + char *undefed = "UNDEFINED"; + char *bridge = "BRIDGE"; + struct saa7164_unit *unit; + int i; + + if (unitid == 0) + return bridge; + + for (i = 0; i < SAA7164_MAX_UNITS; i++) { + unit = &saa7164_boards[dev->board].unit[i]; + + if (unit->type == SAA7164_UNIT_UNDEFINED) + continue; + + if (unitid == unit->id) + return unit->name; + } + + return undefed; +} + diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c new file mode 100644 index 00000000000..c45966edc0c --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-cmd.c @@ -0,0 +1,572 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/wait.h> + +#include "saa7164.h" + +int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev) +{ + int i, ret = -1; + + mutex_lock(&dev->lock); + for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { + if (dev->cmds[i].inuse == 0) { + dev->cmds[i].inuse = 1; + dev->cmds[i].signalled = 0; + dev->cmds[i].timeout = 0; + ret = dev->cmds[i].seqno; + break; + } + } + mutex_unlock(&dev->lock); + + return ret; +} + +void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno) +{ + mutex_lock(&dev->lock); + if ((dev->cmds[seqno].inuse == 1) && + (dev->cmds[seqno].seqno == seqno)) { + dev->cmds[seqno].inuse = 0; + dev->cmds[seqno].signalled = 0; + dev->cmds[seqno].timeout = 0; + } + mutex_unlock(&dev->lock); +} + +void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno) +{ + mutex_lock(&dev->lock); + if ((dev->cmds[seqno].inuse == 1) && + (dev->cmds[seqno].seqno == seqno)) { + dev->cmds[seqno].timeout = 1; + } + mutex_unlock(&dev->lock); +} + +u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno) +{ + int ret = 0; + + mutex_lock(&dev->lock); + if ((dev->cmds[seqno].inuse == 1) && + (dev->cmds[seqno].seqno == seqno)) { + ret = dev->cmds[seqno].timeout; + } + mutex_unlock(&dev->lock); + + return ret; +} + +/* Commands to the f/w get marshelled to/from this code then onto the PCI + * -bus/c running buffer. */ +int saa7164_irq_dequeue(struct saa7164_dev *dev) +{ + int ret = SAA_OK; + u32 timeout; + wait_queue_head_t *q = 0; + dprintk(DBGLVL_CMD, "%s()\n", __func__); + + /* While any outstand message on the bus exists... */ + do { + + /* Peek the msg bus */ + tmComResInfo_t tRsp = { 0, 0, 0, 0, 0, 0 }; + ret = saa7164_bus_get(dev, &tRsp, NULL, 1); + if (ret != SAA_OK) + break; + + q = &dev->cmds[tRsp.seqno].wait; + timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); + dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); + if (!timeout) { + dprintk(DBGLVL_CMD, + "%s() signalled seqno(%d) (for dequeue)\n", + __func__, tRsp.seqno); + dev->cmds[tRsp.seqno].signalled = 1; + wake_up(q); + } else { + printk(KERN_ERR + "%s() found timed out command on the bus\n", + __func__); + } + } while (0); + + return ret; +} + +/* Commands to the f/w get marshelled to/from this code then onto the PCI + * -bus/c running buffer. */ +int saa7164_cmd_dequeue(struct saa7164_dev *dev) +{ + int loop = 1; + int ret; + u32 timeout; + wait_queue_head_t *q = 0; + u8 tmp[512]; + dprintk(DBGLVL_CMD, "%s()\n", __func__); + + while (loop) { + + tmComResInfo_t tRsp = { 0, 0, 0, 0, 0, 0 }; + ret = saa7164_bus_get(dev, &tRsp, NULL, 1); + if (ret == SAA_ERR_EMPTY) + return SAA_OK; + + if (ret != SAA_OK) + return ret; + + q = &dev->cmds[tRsp.seqno].wait; + timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); + dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); + if (timeout) { + printk(KERN_ERR "found timed out command on the bus\n"); + + /* Clean the bus */ + ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); + printk(KERN_ERR "ret = %x\n", ret); + if (ret == SAA_ERR_EMPTY) + /* Someone else already fetched the response */ + return SAA_OK; + + if (ret != SAA_OK) + return ret; + + if (tRsp.flags & PVC_CMDFLAG_CONTINUE) + printk(KERN_ERR "split response\n"); + else + saa7164_cmd_free_seqno(dev, tRsp.seqno); + + printk(KERN_ERR " timeout continue\n"); + continue; + } + + dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n", + __func__, tRsp.seqno); + dev->cmds[tRsp.seqno].signalled = 1; + wake_up(q); + return SAA_OK; + } + + return SAA_OK; +} + +int saa7164_cmd_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf) +{ + tmComResBusInfo_t *bus = &dev->bus; + u8 cmd_sent; + u16 size, idx; + u32 cmds; + void *tmp; + int ret = -1; + + if (!msg) { + printk(KERN_ERR "%s() !msg\n", __func__); + return SAA_ERR_BAD_PARAMETER; + } + + mutex_lock(&dev->cmds[msg->id].lock); + + size = msg->size; + idx = 0; + cmds = size / bus->m_wMaxReqSize; + if (size % bus->m_wMaxReqSize == 0) + cmds -= 1; + + cmd_sent = 0; + + /* Split the request into smaller chunks */ + for (idx = 0; idx < cmds; idx++) { + + msg->flags |= SAA_CMDFLAG_CONTINUE; + msg->size = bus->m_wMaxReqSize; + tmp = buf + idx * bus->m_wMaxReqSize; + + ret = saa7164_bus_set(dev, msg, tmp); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() set failed %d\n", __func__, ret); + + if (cmd_sent) { + ret = SAA_ERR_BUSY; + goto out; + } + ret = SAA_ERR_OVERFLOW; + goto out; + } + cmd_sent = 1; + } + + /* If not the last command... */ + if (idx != 0) + msg->flags &= ~SAA_CMDFLAG_CONTINUE; + + msg->size = size - idx * bus->m_wMaxReqSize; + + ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() set last failed %d\n", __func__, ret); + + if (cmd_sent) { + ret = SAA_ERR_BUSY; + goto out; + } + ret = SAA_ERR_OVERFLOW; + goto out; + } + ret = SAA_OK; + +out: + mutex_unlock(&dev->cmds[msg->id].lock); + return ret; +} + +/* Wait for a signal event, without holding a mutex. Either return TIMEOUT if + * the event never occured, or SAA_OK if it was signaled during the wait. + */ +int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) +{ + wait_queue_head_t *q = 0; + int ret = SAA_BUS_TIMEOUT; + unsigned long stamp; + int r; + + if (saa_debug >= 4) + saa7164_bus_dump(dev); + + dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno); + + mutex_lock(&dev->lock); + if ((dev->cmds[seqno].inuse == 1) && + (dev->cmds[seqno].seqno == seqno)) { + q = &dev->cmds[seqno].wait; + } + mutex_unlock(&dev->lock); + + if (q) { + /* If we haven't been signalled we need to wait */ + if (dev->cmds[seqno].signalled == 0) { + stamp = jiffies; + dprintk(DBGLVL_CMD, + "%s(seqno=%d) Waiting (signalled=%d)\n", + __func__, seqno, dev->cmds[seqno].signalled); + + /* Wait for signalled to be flagged or timeout */ + /* In a highly stressed system this can easily extend + * into multiple seconds before the deferred worker + * is scheduled, and we're woken up via signal. + * We typically are signalled in < 50ms but it can + * take MUCH longer. + */ + wait_event_timeout(*q, dev->cmds[seqno].signalled, (HZ * waitsecs)); + r = time_before(jiffies, stamp + (HZ * waitsecs)); + if (r) + ret = SAA_OK; + else + saa7164_cmd_timeout_seqno(dev, seqno); + + dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d " + "(signalled=%d)\n", __func__, seqno, r, + dev->cmds[seqno].signalled); + } else + ret = SAA_OK; + } else + printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n", + __func__, seqno); + + return ret; +} + +void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno) +{ + int i; + dprintk(DBGLVL_CMD, "%s()\n", __func__); + + mutex_lock(&dev->lock); + for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { + if (dev->cmds[i].inuse == 1) { + dprintk(DBGLVL_CMD, + "seqno %d inuse, sig = %d, t/out = %d\n", + dev->cmds[i].seqno, + dev->cmds[i].signalled, + dev->cmds[i].timeout); + } + } + + for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { + if ((dev->cmds[i].inuse == 1) && ((i == 0) || + (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) { + dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n", + __func__, i); + dev->cmds[i].signalled = 1; + wake_up(&dev->cmds[i].wait); + } + } + mutex_unlock(&dev->lock); +} + +int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, tmComResCmd_t command, + u16 controlselector, u16 size, void *buf) +{ + tmComResInfo_t command_t, *pcommand_t; + tmComResInfo_t response_t, *presponse_t; + u8 errdata[256]; + u16 resp_dsize; + u16 data_recd; + u32 loop; + int ret; + int safety = 0; + + dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, " + "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id, + command, controlselector); + + if ((size == 0) || (buf == 0)) { + printk(KERN_ERR "%s() Invalid param\n", __func__); + return SAA_ERR_BAD_PARAMETER; + } + + /* Prepare some basic command/response structures */ + memset(&command_t, 0, sizeof(command_t)); + memset(&response_t, 0, sizeof(&response_t)); + pcommand_t = &command_t; + presponse_t = &response_t; + command_t.id = id; + command_t.command = command; + command_t.controlselector = controlselector; + command_t.size = size; + + /* Allocate a unique sequence number */ + ret = saa7164_cmd_alloc_seqno(dev); + if (ret < 0) { + printk(KERN_ERR "%s() No free sequences\n", __func__); + ret = SAA_ERR_NO_RESOURCES; + goto out; + } + + command_t.seqno = (u8)ret; + + /* Send Command */ + resp_dsize = size; + pcommand_t->size = size; + + dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n", + __func__, pcommand_t->seqno); + + dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n", + __func__, pcommand_t->size); + + ret = saa7164_cmd_set(dev, pcommand_t, buf); + if (ret != SAA_OK) { + printk(KERN_ERR "%s() set command failed %d\n", __func__, ret); + + if (ret != SAA_ERR_BUSY) + saa7164_cmd_free_seqno(dev, pcommand_t->seqno); + else + /* Flag a timeout, because at least one + * command was sent */ + saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); + + goto out; + } + + /* With split responses we have to collect the msgs piece by piece */ + data_recd = 0; + loop = 1; + while (loop) { + dprintk(DBGLVL_CMD, "%s() loop\n", __func__); + + ret = saa7164_cmd_wait(dev, pcommand_t->seqno); + dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret); + + /* if power is down and this is not a power command ... */ + + if (ret == SAA_BUS_TIMEOUT) { + printk(KERN_ERR "Event timed out\n"); + saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); + return ret; + } + + if (ret != SAA_OK) { + printk(KERN_ERR "spurious error\n"); + return ret; + } + + /* Peek response */ + ret = saa7164_bus_get(dev, presponse_t, NULL, 1); + if (ret == SAA_ERR_EMPTY) { + dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__); + continue; + } + if (ret != SAA_OK) { + printk(KERN_ERR "peek failed\n"); + return ret; + } + + dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n", + __func__, presponse_t->seqno); + + dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n", + __func__, presponse_t->flags); + + dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n", + __func__, presponse_t->size); + + /* Check if the response was for our command */ + if (presponse_t->seqno != pcommand_t->seqno) { + + dprintk(DBGLVL_CMD, + "wrong event: seqno = %d, " + "expected seqno = %d, " + "will dequeue regardless\n", + presponse_t->seqno, pcommand_t->seqno); + + ret = saa7164_cmd_dequeue(dev); + if (ret != SAA_OK) { + printk(KERN_ERR "dequeue failed, ret = %d\n", + ret); + if (safety++ > 16) { + printk(KERN_ERR + "dequeue exceeded, safety exit\n"); + return SAA_ERR_BUSY; + } + } + + continue; + } + + if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) { + + memset(&errdata[0], 0, sizeof(errdata)); + + ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0); + if (ret != SAA_OK) { + printk(KERN_ERR "get error(2)\n"); + return ret; + } + + saa7164_cmd_free_seqno(dev, pcommand_t->seqno); + + dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n", + __func__, errdata[0], errdata[1], errdata[2], + errdata[3]); + + /* Map error codes */ + dprintk(DBGLVL_CMD, "%s() cmd, error code = 0x%x\n", + __func__, errdata[0]); + + switch (errdata[0]) { + case PVC_ERRORCODE_INVALID_COMMAND: + dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n", + __func__); + ret = SAA_ERR_INVALID_COMMAND; + break; + case PVC_ERRORCODE_INVALID_DATA: + dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n", + __func__); + ret = SAA_ERR_BAD_PARAMETER; + break; + case PVC_ERRORCODE_TIMEOUT: + dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__); + ret = SAA_ERR_TIMEOUT; + break; + case PVC_ERRORCODE_NAK: + dprintk(DBGLVL_CMD, "%s() NAK\n", __func__); + ret = SAA_ERR_NULL_PACKET; + break; + case PVC_ERRORCODE_UNKNOWN: + case PVC_ERRORCODE_INVALID_CONTROL: + dprintk(DBGLVL_CMD, + "%s() UNKNOWN OR INVALID CONTROL\n", + __func__); + default: + dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__); + ret = SAA_ERR_NOT_SUPPORTED; + } + + /* See of other commands are on the bus */ + if (saa7164_cmd_dequeue(dev) != SAA_OK) + printk(KERN_ERR "dequeue(2) failed\n"); + + return ret; + } + + /* If response is invalid */ + if ((presponse_t->id != pcommand_t->id) || + (presponse_t->command != pcommand_t->command) || + (presponse_t->controlselector != + pcommand_t->controlselector) || + (((resp_dsize - data_recd) != presponse_t->size) && + !(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) || + ((resp_dsize - data_recd) < presponse_t->size)) { + + /* Invalid */ + dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__); + ret = saa7164_bus_get(dev, presponse_t, 0, 0); + if (ret != SAA_OK) { + printk(KERN_ERR "get failed\n"); + return ret; + } + + /* See of other commands are on the bus */ + if (saa7164_cmd_dequeue(dev) != SAA_OK) + printk(KERN_ERR "dequeue(3) failed\n"); + continue; + } + + /* OK, now we're actually getting out correct response */ + ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0); + if (ret != SAA_OK) { + printk(KERN_ERR "get failed\n"); + return ret; + } + + data_recd = presponse_t->size + data_recd; + if (resp_dsize == data_recd) { + dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__); + break; + } + + /* See of other commands are on the bus */ + if (saa7164_cmd_dequeue(dev) != SAA_OK) + printk(KERN_ERR "dequeue(3) failed\n"); + + continue; + + } /* (loop) */ + + /* Release the sequence number allocation */ + saa7164_cmd_free_seqno(dev, pcommand_t->seqno); + + /* if powerdown signal all pending commands */ + + dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__); + + /* See of other commands are on the bus */ + if (saa7164_cmd_dequeue(dev) != SAA_OK) + printk(KERN_ERR "dequeue(4) failed\n"); + + ret = SAA_OK; +out: + return ret; +} + diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c new file mode 100644 index 00000000000..709affc3104 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -0,0 +1,740 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/div64.h> + +#include "saa7164.h" + +MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); +MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>"); +MODULE_LICENSE("GPL"); + +/* + 1 Basic + 2 + 4 i2c + 8 api + 16 cmd + 32 bus + */ + +unsigned int saa_debug; +module_param_named(debug, saa_debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +unsigned int waitsecs = 10; +module_param(waitsecs, int, 0644); +MODULE_PARM_DESC(debug, "timeout on firmware messages"); + +static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static unsigned int saa7164_devcount; + +static DEFINE_MUTEX(devlist); +LIST_HEAD(saa7164_devlist); + +#define INT_SIZE 16 + +static void saa7164_work_cmdhandler(struct work_struct *w) +{ + struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd); + + /* Wake up any complete commands */ + saa7164_irq_dequeue(dev); +} + +static void saa7164_buffer_deliver(struct saa7164_buffer *buf) +{ + struct saa7164_tsport *port = buf->port; + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu, + SAA7164_TS_NUMBER_OF_LINES); + +} + +static irqreturn_t saa7164_irq_ts(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *buf; + struct list_head *c, *n; + int wp, i = 0, rp; + + /* Find the current write point from the hardware */ + wp = saa7164_readl(port->bufcounter); + if (wp > (port->hwcfg.buffercount - 1)) + BUG(); + + /* Find the previous buffer to the current write point */ + if (wp == 0) + rp = 7; + else + rp = wp - 1; + + /* Lookup the WP in the buffer list */ + /* TODO: turn this into a worker thread */ + list_for_each_safe(c, n, &port->dmaqueue.list) { + buf = list_entry(c, struct saa7164_buffer, list); + if (i++ > port->hwcfg.buffercount) + BUG(); + + if (buf->nr == rp) { + /* Found the buffer, deal with it */ + dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n", + __func__, wp, rp); + saa7164_buffer_deliver(buf); + break; + } + + } + return 0; +} + +/* Primary IRQ handler and dispatch mechanism */ +static irqreturn_t saa7164_irq(int irq, void *dev_id) +{ + struct saa7164_dev *dev = dev_id; + u32 intid, intstat[INT_SIZE/4]; + int i, handled = 0, bit; + + if (dev == 0) { + printk(KERN_ERR "%s() No device specified\n", __func__); + handled = 0; + goto out; + } + + /* Check that the hardware is accessable. If the status bytes are + * 0xFF then the device is not accessable, the the IRQ belongs + * to another driver. + * 4 x u32 interrupt registers. + */ + for (i = 0; i < INT_SIZE/4; i++) { + + /* TODO: Convert into saa7164_readl() */ + /* Read the 4 hardware interrupt registers */ + intstat[i] = saa7164_readl(dev->int_status + (i * 4)); + + if (intstat[i]) + handled = 1; + } + if (handled == 0) + goto out; + + /* For each of the HW interrupt registers */ + for (i = 0; i < INT_SIZE/4; i++) { + + if (intstat[i]) { + /* Each function of the board has it's own interruptid. + * Find the function that triggered then call + * it's handler. + */ + for (bit = 0; bit < 32; bit++) { + + if (((intstat[i] >> bit) & 0x00000001) == 0) + continue; + + /* Calculate the interrupt id (0x00 to 0x7f) */ + + intid = (i * 32) + bit; + if (intid == dev->intfdesc.bInterruptId) { + /* A response to an cmd/api call */ + schedule_work(&dev->workcmd); + } else if (intid == + dev->ts1.hwcfg.interruptid) { + + /* Transport path 1 */ + saa7164_irq_ts(&dev->ts1); + + } else if (intid == + dev->ts2.hwcfg.interruptid) { + + /* Transport path 2 */ + saa7164_irq_ts(&dev->ts2); + + } else { + /* Find the function */ + dprintk(DBGLVL_IRQ, + "%s() unhandled interrupt " + "reg 0x%x bit 0x%x " + "intid = 0x%x\n", + __func__, i, bit, intid); + } + } + + /* Ack it */ + saa7164_writel(dev->int_ack + (i * 4), intstat[i]); + + } + } +out: + return IRQ_RETVAL(handled); +} + +void saa7164_getfirmwarestatus(struct saa7164_dev *dev) +{ + struct saa7164_fw_status *s = &dev->fw_status; + + dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS); + dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE); + dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC); + dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST); + dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD); + dev->fw_status.remainheap = + saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP); + + dprintk(1, "Firmware status:\n"); + dprintk(1, " .status = 0x%08x\n", s->status); + dprintk(1, " .mode = 0x%08x\n", s->mode); + dprintk(1, " .spec = 0x%08x\n", s->spec); + dprintk(1, " .inst = 0x%08x\n", s->inst); + dprintk(1, " .cpuload = 0x%08x\n", s->cpuload); + dprintk(1, " .remainheap = 0x%08x\n", s->remainheap); +} + +u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) +{ + u32 reg; + + reg = saa7164_readl(SAA_DEVICE_VERSION); + dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n", + (reg & 0x0000fc00) >> 10, + (reg & 0x000003e0) >> 5, + (reg & 0x0000001f), + (reg & 0xffff0000) >> 16, + reg); + + return reg; +} + +/* TODO: Debugging func, remove */ +void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len) +{ + int i; + + printk(KERN_INFO "--------------------> " + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + + for (i = 0; i < len; i += 16) + printk(KERN_INFO " [0x%08x] " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, + *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3), + *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7), + *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11), + *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15)); +} + +/* TODO: Debugging func, remove */ +void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) +{ + int i; + + dprintk(1, "--------------------> " + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + + for (i = 0; i < 0x100; i += 16) + dprintk(1, "region0[0x%08x] = " + "%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", i, + (u8)saa7164_readb(addr + i + 0), + (u8)saa7164_readb(addr + i + 1), + (u8)saa7164_readb(addr + i + 2), + (u8)saa7164_readb(addr + i + 3), + (u8)saa7164_readb(addr + i + 4), + (u8)saa7164_readb(addr + i + 5), + (u8)saa7164_readb(addr + i + 6), + (u8)saa7164_readb(addr + i + 7), + (u8)saa7164_readb(addr + i + 8), + (u8)saa7164_readb(addr + i + 9), + (u8)saa7164_readb(addr + i + 10), + (u8)saa7164_readb(addr + i + 11), + (u8)saa7164_readb(addr + i + 12), + (u8)saa7164_readb(addr + i + 13), + (u8)saa7164_readb(addr + i + 14), + (u8)saa7164_readb(addr + i + 15) + ); +} + +static void saa7164_dump_hwdesc(struct saa7164_dev *dev) +{ + dprintk(1, "@0x%p hwdesc sizeof(tmComResHWDescr_t) = %d bytes\n", + &dev->hwdesc, (u32)sizeof(tmComResHWDescr_t)); + + dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength); + dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType); + dprintk(1, " .bDescriptorSubtype = 0x%x\n", + dev->hwdesc.bDescriptorSubtype); + + dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion); + dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency); + dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes); + dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities); + dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n", + dev->hwdesc.dwDeviceRegistersLocation); + + dprintk(1, " .dwHostMemoryRegion = 0x%x\n", + dev->hwdesc.dwHostMemoryRegion); + + dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n", + dev->hwdesc.dwHostMemoryRegionSize); + + dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n", + dev->hwdesc.dwHostHibernatMemRegion); + + dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n", + dev->hwdesc.dwHostHibernatMemRegionSize); +} + +static void saa7164_dump_intfdesc(struct saa7164_dev *dev) +{ + dprintk(1, "@0x%p intfdesc " + "sizeof(tmComResInterfaceDescr_t) = %d bytes\n", + &dev->intfdesc, (u32)sizeof(tmComResInterfaceDescr_t)); + + dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength); + dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType); + dprintk(1, " .bDescriptorSubtype = 0x%x\n", + dev->intfdesc.bDescriptorSubtype); + + dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags); + dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType); + dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId); + dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface); + dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId); + dprintk(1, " .bDebugInterruptId = 0x%x\n", + dev->intfdesc.bDebugInterruptId); + + dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation); +} + +static void saa7164_dump_busdesc(struct saa7164_dev *dev) +{ + dprintk(1, "@0x%p busdesc sizeof(tmComResBusDescr_t) = %d bytes\n", + &dev->busdesc, (u32)sizeof(tmComResBusDescr_t)); + + dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing); + dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing); + dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite); + dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead); + dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite); + dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead); +} + +/* Much of the hardware configuration and PCI registers are configured + * dynamically depending on firmware. We have to cache some initial + * structures then use these to locate other important structures + * from PCI space. + */ +static void saa7164_get_descriptors(struct saa7164_dev *dev) +{ + memcpy(&dev->hwdesc, dev->bmmio, sizeof(tmComResHWDescr_t)); + memcpy(&dev->intfdesc, dev->bmmio + sizeof(tmComResHWDescr_t), + sizeof(tmComResInterfaceDescr_t)); + memcpy(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation, + sizeof(tmComResBusDescr_t)); + + if (dev->hwdesc.bLength != sizeof(tmComResHWDescr_t)) { + printk(KERN_ERR "Structure tmComResHWDescr_t is mangled\n"); + printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength, + (u32)sizeof(tmComResHWDescr_t)); + } else + saa7164_dump_hwdesc(dev); + + if (dev->intfdesc.bLength != sizeof(tmComResInterfaceDescr_t)) { + printk(KERN_ERR "struct tmComResInterfaceDescr_t is mangled\n"); + printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength, + (u32)sizeof(tmComResInterfaceDescr_t)); + } else + saa7164_dump_intfdesc(dev); + + saa7164_dump_busdesc(dev); +} + +static int saa7164_pci_quirks(struct saa7164_dev *dev) +{ + return 0; +} + +static int get_resources(struct saa7164_dev *dev) +{ + if (request_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0), dev->name)) { + + if (request_mem_region(pci_resource_start(dev->pci, 2), + pci_resource_len(dev->pci, 2), dev->name)) + return 0; + } + + printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", + dev->name, + (u64)pci_resource_start(dev->pci, 0), + (u64)pci_resource_start(dev->pci, 2)); + + return -EBUSY; +} + +static int saa7164_dev_setup(struct saa7164_dev *dev) +{ + int i; + + mutex_init(&dev->lock); + atomic_inc(&dev->refcount); + dev->nr = saa7164_devcount++; + + sprintf(dev->name, "saa7164[%d]", dev->nr); + + mutex_lock(&devlist); + list_add_tail(&dev->devlist, &saa7164_devlist); + mutex_unlock(&devlist); + + /* board config */ + dev->board = UNSET; + if (card[dev->nr] < saa7164_bcount) + dev->board = card[dev->nr]; + + for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++) + if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor && + dev->pci->subsystem_device == + saa7164_subids[i].subdevice) + dev->board = saa7164_subids[i].card; + + if (UNSET == dev->board) { + dev->board = SAA7164_BOARD_UNKNOWN; + saa7164_card_list(dev); + } + + dev->pci_bus = dev->pci->bus->number; + dev->pci_slot = PCI_SLOT(dev->pci->devfn); + + /* I2C Defaults / setup */ + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[1].dev = dev; + dev->i2c_bus[1].nr = 1; + dev->i2c_bus[2].dev = dev; + dev->i2c_bus[2].nr = 2; + + /* Transport port A Defaults / setup */ + dev->ts1.dev = dev; + dev->ts1.nr = 0; + mutex_init(&dev->ts1.dvb.lock); + INIT_LIST_HEAD(&dev->ts1.dmaqueue.list); + INIT_LIST_HEAD(&dev->ts1.dummy_dmaqueue.list); + mutex_init(&dev->ts1.dmaqueue_lock); + mutex_init(&dev->ts1.dummy_dmaqueue_lock); + + /* Transport port B Defaults / setup */ + dev->ts2.dev = dev; + dev->ts2.nr = 1; + mutex_init(&dev->ts2.dvb.lock); + INIT_LIST_HEAD(&dev->ts2.dmaqueue.list); + INIT_LIST_HEAD(&dev->ts2.dummy_dmaqueue.list); + mutex_init(&dev->ts2.dmaqueue_lock); + mutex_init(&dev->ts2.dummy_dmaqueue_lock); + + if (get_resources(dev) < 0) { + printk(KERN_ERR "CORE %s No more PCIe resources for " + "subsystem: %04x:%04x\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device); + + saa7164_devcount--; + return -ENODEV; + } + + /* PCI/e allocations */ + dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + + dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), + pci_resource_len(dev->pci, 2)); + + dev->bmmio = (u8 __iomem *)dev->lmmio; + dev->bmmio2 = (u8 __iomem *)dev->lmmio2; + + /* Inerrupt and ack register locations offset of bmmio */ + dev->int_status = 0x183000 + 0xf80; + dev->int_ack = 0x183000 + 0xf90; + + printk(KERN_INFO + "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device, saa7164_boards[dev->board].name, + dev->board, card[dev->nr] == dev->board ? + "insmod option" : "autodetected"); + + saa7164_pci_quirks(dev); + + return 0; +} + +static void saa7164_dev_unregister(struct saa7164_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + + release_mem_region(pci_resource_start(dev->pci, 2), + pci_resource_len(dev->pci, 2)); + + if (!atomic_dec_and_test(&dev->refcount)) + return; + + iounmap(dev->lmmio); + iounmap(dev->lmmio2); + + return; +} + +static int __devinit saa7164_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct saa7164_dev *dev; + int err, i; + u32 version; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail_free; + } + + if (saa7164_dev_setup(dev) < 0) { + err = -EINVAL; + goto fail_free; + } + + /* print pci info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%llx\n", dev->name, + pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat, + (unsigned long long)pci_resource_start(pci_dev, 0)); + + pci_set_master(pci_dev); + /* TODO */ + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); + err = -EIO; + goto fail_irq; + } + + err = request_irq(pci_dev->irq, saa7164_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, + pci_dev->irq); + err = -EIO; + goto fail_irq; + } + + pci_set_drvdata(pci_dev, dev); + + /* Init the internal command list */ + for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { + dev->cmds[i].seqno = i; + dev->cmds[i].inuse = 0; + mutex_init(&dev->cmds[i].lock); + init_waitqueue_head(&dev->cmds[i].wait); + } + + /* We need a deferred interrupt handler for cmd handling */ + INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler); + + /* Only load the firmware if we know the board */ + if (dev->board != SAA7164_BOARD_UNKNOWN) { + + err = saa7164_downloadfirmware(dev); + if (err < 0) { + printk(KERN_ERR + "Failed to boot firmware, no features " + "registered\n"); + goto fail_fw; + } + + saa7164_get_descriptors(dev); + saa7164_dumpregs(dev, 0); + saa7164_getcurrentfirmwareversion(dev); + saa7164_getfirmwarestatus(dev); + err = saa7164_bus_setup(dev); + if (err < 0) + printk(KERN_ERR + "Failed to setup the bus, will continue\n"); + saa7164_bus_dump(dev); + + /* Ping the running firmware via the command bus and get the + * firmware version, this checks the bus is running OK. + */ + version = 0; + if (saa7164_api_get_fw_version(dev, &version) == SAA_OK) + dprintk(1, "Bus is operating correctly using " + "version %d.%d.%d.%d (0x%x)\n", + (version & 0x0000fc00) >> 10, + (version & 0x000003e0) >> 5, + (version & 0x0000001f), + (version & 0xffff0000) >> 16, + version); + else + printk(KERN_ERR + "Failed to communicate with the firmware\n"); + + /* Bring up the I2C buses */ + saa7164_i2c_register(&dev->i2c_bus[0]); + saa7164_i2c_register(&dev->i2c_bus[1]); + saa7164_i2c_register(&dev->i2c_bus[2]); + saa7164_gpio_setup(dev); + saa7164_card_setup(dev); + + + /* Parse the dynamic device configuration, find various + * media endpoints (MPEG, WMV, PS, TS) and cache their + * configuration details into the driver, so we can + * reference them later during simething_register() func, + * interrupt handlers, deferred work handlers etc. + */ + saa7164_api_enum_subdevs(dev); + + /* Begin to create the video sub-systems and register funcs */ + if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) { + if (saa7164_dvb_register(&dev->ts1) < 0) { + printk(KERN_ERR "%s() Failed to register " + "dvb adapters on porta\n", + __func__); + } + } + + if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) { + if (saa7164_dvb_register(&dev->ts2) < 0) { + printk(KERN_ERR"%s() Failed to register " + "dvb adapters on portb\n", + __func__); + } + } + + } /* != BOARD_UNKNOWN */ + else + printk(KERN_ERR "%s() Unsupported board detected, " + "registering without firmware\n", __func__); + + dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug); + dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs); + +fail_fw: + return 0; + +fail_irq: + saa7164_dev_unregister(dev); +fail_free: + kfree(dev); + return err; +} + +static void saa7164_shutdown(struct saa7164_dev *dev) +{ + dprintk(1, "%s()\n", __func__); +} + +static void __devexit saa7164_finidev(struct pci_dev *pci_dev) +{ + struct saa7164_dev *dev = pci_get_drvdata(pci_dev); + + saa7164_shutdown(dev); + + if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) + saa7164_dvb_unregister(&dev->ts1); + + if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) + saa7164_dvb_unregister(&dev->ts2); + + saa7164_i2c_unregister(&dev->i2c_bus[0]); + saa7164_i2c_unregister(&dev->i2c_bus[1]); + saa7164_i2c_unregister(&dev->i2c_bus[2]); + + pci_disable_device(pci_dev); + + /* unregister stuff */ + free_irq(pci_dev->irq, dev); + pci_set_drvdata(pci_dev, NULL); + + mutex_lock(&devlist); + list_del(&dev->devlist); + mutex_unlock(&devlist); + + saa7164_dev_unregister(dev); + kfree(dev); +} + +static struct pci_device_id saa7164_pci_tbl[] = { + { + /* SAA7164 */ + .vendor = 0x1131, + .device = 0x7164, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl); + +static struct pci_driver saa7164_pci_driver = { + .name = "saa7164", + .id_table = saa7164_pci_tbl, + .probe = saa7164_initdev, + .remove = __devexit_p(saa7164_finidev), + /* TODO */ + .suspend = NULL, + .resume = NULL, +}; + +static int saa7164_init(void) +{ + printk(KERN_INFO "saa7164 driver loaded\n"); + return pci_register_driver(&saa7164_pci_driver); +} + +static void saa7164_fini(void) +{ + pci_unregister_driver(&saa7164_pci_driver); +} + +module_init(saa7164_init); +module_exit(saa7164_fini); + diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c new file mode 100644 index 00000000000..6a2d847d6a8 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -0,0 +1,602 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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 "saa7164.h" + +#include "tda10048.h" +#include "tda18271.h" +#include "s5h1411.h" + +#define DRIVER_NAME "saa7164" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* addr is in the card struct, get it from there */ +static struct tda10048_config hauppauge_hvr2200_1_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, +}; +static struct tda10048_config hauppauge_hvr2200_2_config = { + .demod_address = 0x12 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37 }, +}; + +static struct tda18271_config hauppauge_hvr22x0_tuner_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .role = TDA18271_MASTER, +}; + +static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .role = TDA18271_SLAVE, + .rf_cal_on_startup = 1 +}; + +static struct s5h1411_config hauppauge_s5h1411_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_ON, + .qam_if = S5H1411_IF_4000, + .vsb_if = S5H1411_IF_3250, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + +static int saa7164_dvb_stop_tsport(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + int ret; + + ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); + if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", + __func__, ret); + ret = -EIO; + } else { + dprintk(DBGLVL_DVB, "%s() Stopped\n", __func__); + ret = 0; + } + + return ret; +} + +static int saa7164_dvb_acquire_tsport(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + int ret; + + ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); + if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", + __func__, ret); + ret = -EIO; + } else { + dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); + ret = 0; + } + + return ret; +} + +static int saa7164_dvb_pause_tsport(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + int ret; + + ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); + if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", + __func__, ret); + ret = -EIO; + } else { + dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); + ret = 0; + } + + return ret; +} + +/* Firmware is very windows centric, meaning you have to transition + * the part through AVStream / KS Windows stages, forwards or backwards. + * States are: stopped, acquired (h/w), paused, started. + */ +static int saa7164_dvb_stop_streaming(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + int ret; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + ret = saa7164_dvb_pause_tsport(port); + ret = saa7164_dvb_acquire_tsport(port); + ret = saa7164_dvb_stop_tsport(port); + + return ret; +} + +static int saa7164_dvb_cfg_tsport(struct saa7164_tsport *port) +{ + tmHWStreamParameters_t *params = &port->hw_streamingparams; + struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *buf; + struct list_head *c, *n; + int i = 0; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + saa7164_writel(port->pitch, params->pitch); + saa7164_writel(port->bufsize, params->pitch * params->numberoflines); + + dprintk(DBGLVL_DVB, " configured:\n"); + dprintk(DBGLVL_DVB, " lmmio 0x%p\n", dev->lmmio); + dprintk(DBGLVL_DVB, " bufcounter 0x%x = 0x%x\n", port->bufcounter, + saa7164_readl(port->bufcounter)); + + dprintk(DBGLVL_DVB, " pitch 0x%x = %d\n", port->pitch, + saa7164_readl(port->pitch)); + + dprintk(DBGLVL_DVB, " bufsize 0x%x = %d\n", port->bufsize, + saa7164_readl(port->bufsize)); + + dprintk(DBGLVL_DVB, " buffercount = %d\n", port->hwcfg.buffercount); + dprintk(DBGLVL_DVB, " bufoffset = 0x%x\n", port->bufoffset); + dprintk(DBGLVL_DVB, " bufptr32h = 0x%x\n", port->bufptr32h); + dprintk(DBGLVL_DVB, " bufptr32l = 0x%x\n", port->bufptr32l); + + /* Poke the buffers and offsets into PCI space */ + mutex_lock(&port->dmaqueue_lock); + list_for_each_safe(c, n, &port->dmaqueue.list) { + buf = list_entry(c, struct saa7164_buffer, list); + + /* TODO: Review this in light of 32v64 assignments */ + saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); + saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), + buf->pt_dma); + saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); + + dprintk(DBGLVL_DVB, + " buf[%d] offset 0x%llx (0x%x) " + "buf 0x%llx/%llx (0x%x/%x)\n", + i, + (u64)port->bufoffset + (i * sizeof(u32)), + saa7164_readl(port->bufoffset + (sizeof(u32) * i)), + (u64)port->bufptr32h + ((sizeof(u32) * 2) * i), + (u64)port->bufptr32l + ((sizeof(u32) * 2) * i), + saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) + * 2)), + saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) + * 2))); + + if (i++ > port->hwcfg.buffercount) + BUG(); + + } + mutex_unlock(&port->dmaqueue_lock); + + return 0; +} + +static int saa7164_dvb_start_tsport(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + int ret = 0, result; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + saa7164_dvb_cfg_tsport(port); + + /* Acquire the hardware */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", + __func__, result); + + /* Stop the hardware, regardless */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() acquire/forced stop transition " + "failed, res = 0x%x\n", __func__, result); + } + ret = -EIO; + goto out; + } else + dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); + + /* Pause the hardware */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", + __func__, result); + + /* Stop the hardware, regardless */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() pause/forced stop transition " + "failed, res = 0x%x\n", __func__, result); + } + + ret = -EIO; + goto out; + } else + dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); + + /* Start the hardware */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", + __func__, result); + + /* Stop the hardware, regardless */ + result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); + if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + printk(KERN_ERR "%s() run/forced stop transition " + "failed, res = 0x%x\n", __func__, result); + } + + ret = -EIO; + } else + dprintk(DBGLVL_DVB, "%s() Running\n", __func__); + +out: + return ret; +} + +static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv; + struct saa7164_dvb *dvb = &port->dvb; + struct saa7164_dev *dev = port->dev; + int ret = 0; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + ret = saa7164_dvb_start_tsport(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); + } + + return ret; +} + +static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv; + struct saa7164_dvb *dvb = &port->dvb; + struct saa7164_dev *dev = port->dev; + int ret = 0; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + ret = saa7164_dvb_stop_streaming(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); + } + + return ret; +} + +static int dvb_register(struct saa7164_tsport *port) +{ + struct saa7164_dvb *dvb = &port->dvb; + struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *buf; + int result, i; + + dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); + + /* Sanity check that the PCI configuration space is active */ + if (port->hwcfg.BARLocation == 0) { + result = -ENOMEM; + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d), NO PCI configuration\n", + DRIVER_NAME, result); + goto fail_adapter; + } + + /* Init and establish defaults */ + port->hw_streamingparams.bitspersample = 8; + port->hw_streamingparams.samplesperline = 188; + port->hw_streamingparams.numberoflines = + (SAA7164_TS_NUMBER_OF_LINES * 188) / 188; + + port->hw_streamingparams.pitch = 188; + port->hw_streamingparams.linethreshold = 0; + port->hw_streamingparams.pagetablelistvirt = 0; + port->hw_streamingparams.pagetablelistphys = 0; + port->hw_streamingparams.numpagetables = 2 + + ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); + + port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount; + + /* Allocate the PCI resources */ + for (i = 0; i < port->hwcfg.buffercount; i++) { + buf = saa7164_buffer_alloc(port, + port->hw_streamingparams.numberoflines * + port->hw_streamingparams.pitch); + + if (!buf) { + result = -ENOMEM; + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d), unable to allocate buffers\n", + DRIVER_NAME, result); + goto fail_adapter; + } + buf->nr = i; + + mutex_lock(&port->dmaqueue_lock); + list_add_tail(&buf->list, &port->dmaqueue.list); + mutex_unlock(&port->dmaqueue_lock); + } + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + &dev->pci->dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_adapter; + } + dvb->adapter.priv = port; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_frontend failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = port; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = saa7164_dvb_start_feed; + dvb->demux.stop_feed = saa7164_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +int saa7164_dvb_unregister(struct saa7164_tsport *port) +{ + struct saa7164_dvb *dvb = &port->dvb; + struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *b; + struct list_head *c, *n; + + dprintk(DBGLVL_DVB, "%s()\n", __func__); + + /* Remove any allocated buffers */ + mutex_lock(&port->dmaqueue_lock); + list_for_each_safe(c, n, &port->dmaqueue.list) { + b = list_entry(c, struct saa7164_buffer, list); + list_del(c); + saa7164_buffer_dealloc(port, b); + } + mutex_unlock(&port->dmaqueue_lock); + + if (dvb->frontend == NULL) + return 0; + + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); + return 0; +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. + */ +int saa7164_dvb_register(struct saa7164_tsport *port) +{ + struct saa7164_dev *dev = port->dev; + struct saa7164_dvb *dvb = &port->dvb; + struct saa7164_i2c *i2c_bus = NULL; + int ret; + + dprintk(DBGLVL_DVB, "%s()\n", __func__); + + /* init frontend */ + switch (dev->board) { + case SAA7164_BOARD_HAUPPAUGE_HVR2200: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + i2c_bus = &dev->i2c_bus[port->nr + 1]; + switch (port->nr) { + case 0: + port->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr2200_1_config, + &i2c_bus->i2c_adap); + + if (port->dvb.frontend != NULL) { + /* TODO: addr is in the card struct */ + dvb_attach(tda18271_attach, port->dvb.frontend, + 0xc0 >> 1, &i2c_bus->i2c_adap, + &hauppauge_hvr22x0_tuner_config); + } + + break; + case 1: + port->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr2200_2_config, + &i2c_bus->i2c_adap); + + if (port->dvb.frontend != NULL) { + /* TODO: addr is in the card struct */ + dvb_attach(tda18271_attach, port->dvb.frontend, + 0xc0 >> 1, &i2c_bus->i2c_adap, + &hauppauge_hvr22x0s_tuner_config); + } + + break; + } + break; + case SAA7164_BOARD_HAUPPAUGE_HVR2250: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: + case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + i2c_bus = &dev->i2c_bus[port->nr + 1]; + + port->dvb.frontend = dvb_attach(s5h1411_attach, + &hauppauge_s5h1411_config, + &i2c_bus->i2c_adap); + + if (port->dvb.frontend != NULL) { + if (port->nr == 0) { + /* Master TDA18271 */ + /* TODO: addr is in the card struct */ + dvb_attach(tda18271_attach, port->dvb.frontend, + 0xc0 >> 1, &i2c_bus->i2c_adap, + &hauppauge_hvr22x0_tuner_config); + } else { + /* Slave TDA18271 */ + dvb_attach(tda18271_attach, port->dvb.frontend, + 0xc0 >> 1, &i2c_bus->i2c_adap, + &hauppauge_hvr22x0s_tuner_config); + } + } + + break; + default: + printk(KERN_ERR "%s: The frontend isn't supported\n", + dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR "%s() Frontend initialization failed\n", + __func__); + return -1; + } + + /* Put the analog decoder in standby to keep it quiet */ + + /* register everything */ + ret = dvb_register(port); + if (ret < 0) { + if (dvb->frontend->ops.release) + dvb->frontend->ops.release(dvb->frontend); + return ret; + } + + return 0; +} + diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c new file mode 100644 index 00000000000..ee0af3534ed --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-fw.c @@ -0,0 +1,613 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/firmware.h> + +#include "saa7164.h" + +#define SAA7164_REV2_FIRMWARE "v4l-saa7164-1.0.2.fw" +#define SAA7164_REV2_FIRMWARE_SIZE 3978608 + +#define SAA7164_REV3_FIRMWARE "v4l-saa7164-1.0.3.fw" +#define SAA7164_REV3_FIRMWARE_SIZE 3978608 + +struct fw_header { + u32 firmwaresize; + u32 bslsize; + u32 reserved; + u32 version; +}; + +int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg) +{ + u32 timeout = SAA_DEVICE_TIMEOUT; + while ((saa7164_readl(reg) & 0x01) == 0) { + timeout -= 10; + if (timeout == 0) { + printk(KERN_ERR "%s() timeout (no d/l ack)\n", + __func__); + return -EBUSY; + } + msleep(100); + } + + return 0; +} + +int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg) +{ + u32 timeout = SAA_DEVICE_TIMEOUT; + while (saa7164_readl(reg) & 0x01) { + timeout -= 10; + if (timeout == 0) { + printk(KERN_ERR "%s() timeout (no d/l clr)\n", + __func__); + return -EBUSY; + } + msleep(100); + } + + return 0; +} + +/* TODO: move dlflags into dev-> and change to write/readl/b */ +/* TODO: Excessive levels of debug */ +int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, + u32 dlflags, u8 *dst, u32 dstsize) +{ + u32 reg, timeout, offset; + u8 *srcbuf = NULL; + int ret; + + u32 dlflag = dlflags; + u32 dlflag_ack = dlflag + 4; + u32 drflag = dlflag_ack + 4; + u32 drflag_ack = drflag + 4; + u32 bleflag = drflag_ack + 4; + + dprintk(DBGLVL_FW, + "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", + __func__, src, srcsize, dlflags, dst, dstsize); + + if ((src == 0) || (dst == 0)) { + ret = -EIO; + goto out; + } + + srcbuf = kzalloc(4 * 1048576, GFP_KERNEL); + if (NULL == srcbuf) { + ret = -ENOMEM; + goto out; + } + + if (srcsize > (4*1048576)) { + ret = -ENOMEM; + goto out; + } + + memcpy(srcbuf, src, srcsize); + + dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag); + dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack); + dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag); + dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack); + dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag); + + reg = saa7164_readl(dlflag); + dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg); + if (reg == 1) + dprintk(DBGLVL_FW, + "%s() Download flag already set, please reboot\n", + __func__); + + /* Indicate download start */ + saa7164_writel(dlflag, 1); + ret = saa7164_dl_wait_ack(dev, dlflag_ack); + if (ret < 0) + goto out; + + /* Ack download start, then wait for wait */ + saa7164_writel(dlflag, 0); + ret = saa7164_dl_wait_clr(dev, dlflag_ack); + if (ret < 0) + goto out; + + /* Deal with the raw firmware, in the appropriate chunk size */ + for (offset = 0; srcsize > dstsize; + srcsize -= dstsize, offset += dstsize) { + + dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize); + memcpy(dst, srcbuf + offset, dstsize); + + /* Flag the data as ready */ + saa7164_writel(drflag, 1); + ret = saa7164_dl_wait_ack(dev, drflag_ack); + if (ret < 0) + goto out; + + /* Wait for indication data was received */ + saa7164_writel(drflag, 0); + ret = saa7164_dl_wait_clr(dev, drflag_ack); + if (ret < 0) + goto out; + + } + + dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize); + /* Write last block to the device */ + memcpy(dst, srcbuf+offset, srcsize); + + /* Flag the data as ready */ + saa7164_writel(drflag, 1); + ret = saa7164_dl_wait_ack(dev, drflag_ack); + if (ret < 0) + goto out; + + saa7164_writel(drflag, 0); + timeout = 0; + while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) { + if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) { + printk(KERN_ERR "%s() image corrupt\n", __func__); + ret = -EBUSY; + goto out; + } + + if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) { + printk(KERN_ERR "%s() device memory corrupt\n", + __func__); + ret = -EBUSY; + goto out; + } + + msleep(10); + if (timeout++ > 60) + break; + } + + printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__); + + ret = saa7164_dl_wait_clr(dev, drflag_ack); + if (ret < 0) + goto out; + + printk(KERN_INFO "%s() Image booted successfully.\n", __func__); + ret = 0; + +out: + kfree(srcbuf); + return ret; +} + +/* TODO: Excessive debug */ +/* Load the firmware. Optionally it can be in ROM or newer versions + * can be on disk, saving the expense of the ROM hardware. */ +int saa7164_downloadfirmware(struct saa7164_dev *dev) +{ + /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */ + u32 tmp, filesize, version, err_flags, first_timeout, fwlength; + u32 second_timeout, updatebootloader = 1, bootloadersize = 0; + const struct firmware *fw = NULL; + struct fw_header *hdr, *boothdr = NULL, *fwhdr; + u32 bootloaderversion = 0, fwloadersize; + u8 *bootloaderoffset = NULL, *fwloaderoffset; + char *fwname; + int ret; + + dprintk(DBGLVL_FW, "%s()\n", __func__); + + if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) { + fwname = SAA7164_REV2_FIRMWARE; + fwlength = SAA7164_REV2_FIRMWARE_SIZE; + } else { + fwname = SAA7164_REV3_FIRMWARE; + fwlength = SAA7164_REV3_FIRMWARE_SIZE; + } + + version = saa7164_getcurrentfirmwareversion(dev); + + if (version == 0x00) { + + second_timeout = 100; + first_timeout = 100; + err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); + dprintk(DBGLVL_FW, "%s() err_flags = %x\n", + __func__, err_flags); + + while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { + dprintk(DBGLVL_FW, "%s() err_flags = %x\n", + __func__, err_flags); + msleep(10); + + if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { + printk(KERN_ERR "%s() firmware corrupt\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { + printk(KERN_ERR "%s() device memory corrupt\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_NO_IMAGE) { + printk(KERN_ERR "%s() no first image\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { + first_timeout -= 10; + if (first_timeout == 0) { + printk(KERN_ERR + "%s() no first image\n", + __func__); + break; + } + } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) { + second_timeout -= 10; + if (second_timeout == 0) { + printk(KERN_ERR + "%s() FW load time exceeded\n", + __func__); + break; + } + } else { + second_timeout -= 10; + if (second_timeout == 0) { + printk(KERN_ERR + "%s() Unknown bootloader flags 0x%x\n", + __func__, err_flags); + break; + } + } + + err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); + } /* While != Booting */ + + if (err_flags == SAA_DEVICE_IMAGE_BOOTING) { + dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", + __func__); + first_timeout = SAA_DEVICE_TIMEOUT; + second_timeout = 60 * SAA_DEVICE_TIMEOUT; + second_timeout = 100; + + err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); + dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", + __func__, err_flags); + while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { + dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", + __func__, err_flags); + msleep(10); + + if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { + printk(KERN_ERR + "%s() firmware corrupt\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { + printk(KERN_ERR + "%s() device memory corrupt\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_NO_IMAGE) { + printk(KERN_ERR "%s() no first image\n", + __func__); + break; + } + if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { + first_timeout -= 10; + if (first_timeout == 0) { + printk(KERN_ERR + "%s() no second image\n", + __func__); + break; + } + } else if (err_flags & + SAA_DEVICE_IMAGE_LOADING) { + second_timeout -= 10; + if (second_timeout == 0) { + printk(KERN_ERR + "%s() FW load time exceeded\n", + __func__); + break; + } + } else { + second_timeout -= 10; + if (second_timeout == 0) { + printk(KERN_ERR + "%s() Unknown bootloader flags 0x%x\n", + __func__, err_flags); + break; + } + } + + err_flags = + saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); + } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */ + + dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n", + __func__, + saa7164_readl(SAA_BOOTLOADERERROR_FLAGS), + saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS)); + + } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */ + + /* It's possible for both firmwares to have booted, + * but that doesn't mean they've finished booting yet. + */ + if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == + SAA_DEVICE_IMAGE_BOOTING) && + (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == + SAA_DEVICE_IMAGE_BOOTING)) { + + + dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n", + __func__); + + first_timeout = SAA_DEVICE_TIMEOUT; + while (first_timeout) { + msleep(10); + + version = + saa7164_getcurrentfirmwareversion(dev); + if (version) { + dprintk(DBGLVL_FW, + "%s() All f/w loaded successfully\n", + __func__); + break; + } else { + first_timeout -= 10; + if (first_timeout == 0) { + printk(KERN_ERR + "%s() FW did not boot\n", + __func__); + break; + } + } + } + } + version = saa7164_getcurrentfirmwareversion(dev); + } /* version == 0 */ + + /* Has the firmware really booted? */ + if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == + SAA_DEVICE_IMAGE_BOOTING) && + (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == + SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) { + + printk(KERN_ERR + "%s() The firmware hung, probably bad firmware\n", + __func__); + + /* Tell the second stage loader we have a deadlock */ + saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET, + SAA_DEVICE_DEADLOCK_DETECTED); + + saa7164_getfirmwarestatus(dev); + + return -ENOMEM; + } + + dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n", + (version & 0x0000fc00) >> 10, + (version & 0x000003e0) >> 5, + (version & 0x0000001f), + (version & 0xffff0000) >> 16); + + /* Load the firmwware from the disk if required */ + if (version == 0) { + + printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n", + __func__, fwname); + + ret = request_firmware(&fw, fwname, &dev->pci->dev); + if (ret) { + printk(KERN_ERR "%s() Upload failed. " + "(file not found?)\n", __func__); + return -ENOMEM; + } + + printk(KERN_INFO "%s() firmware read %Zu bytes.\n", + __func__, fw->size); + + if (fw->size != fwlength) { + printk(KERN_ERR "xc5000: firmware incorrect size\n"); + ret = -ENOMEM; + goto out; + } + + printk(KERN_INFO "%s() firmware loaded.\n", __func__); + + hdr = (struct fw_header *)fw->data; + printk(KERN_INFO "Firmware file header part 1:\n"); + printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize); + printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize); + printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved); + printk(KERN_INFO " .Version = 0x%x\n", hdr->version); + + /* Retreive bootloader if reqd */ + if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) + /* Second bootloader in the firmware file */ + filesize = hdr->reserved * 16; + else + filesize = (hdr->firmwaresize + hdr->bslsize) * + 16 + sizeof(struct fw_header); + + printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n", + __func__, filesize); + + /* Get bootloader (if reqd) and firmware header */ + if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { + /* Second boot loader is required */ + + /* Get the loader header */ + boothdr = (struct fw_header *)(fw->data + + sizeof(struct fw_header)); + + bootloaderversion = + saa7164_readl(SAA_DEVICE_2ND_VERSION); + dprintk(DBGLVL_FW, "Onboard BootLoader:\n"); + dprintk(DBGLVL_FW, "->Flag 0x%x\n", + saa7164_readl(SAA_BOOTLOADERERROR_FLAGS)); + dprintk(DBGLVL_FW, "->Ack 0x%x\n", + saa7164_readl(SAA_DATAREADY_FLAG_ACK)); + dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version); + dprintk(DBGLVL_FW, "->Loader Version 0x%x\n", + bootloaderversion); + + if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == + 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK) + == 0x00) && (version == 0x00)) { + + dprintk(DBGLVL_FW, "BootLoader version in " + "rom %d.%d.%d.%d\n", + (bootloaderversion & 0x0000fc00) >> 10, + (bootloaderversion & 0x000003e0) >> 5, + (bootloaderversion & 0x0000001f), + (bootloaderversion & 0xffff0000) >> 16 + ); + dprintk(DBGLVL_FW, "BootLoader version " + "in file %d.%d.%d.%d\n", + (boothdr->version & 0x0000fc00) >> 10, + (boothdr->version & 0x000003e0) >> 5, + (boothdr->version & 0x0000001f), + (boothdr->version & 0xffff0000) >> 16 + ); + + if (bootloaderversion == boothdr->version) + updatebootloader = 0; + } + + /* Calculate offset to firmware header */ + tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 + + (sizeof(struct fw_header) + + sizeof(struct fw_header)); + + fwhdr = (struct fw_header *)(fw->data+tmp); + } else { + /* No second boot loader */ + fwhdr = hdr; + } + + dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n", + (fwhdr->version & 0x0000fc00) >> 10, + (fwhdr->version & 0x000003e0) >> 5, + (fwhdr->version & 0x0000001f), + (fwhdr->version & 0xffff0000) >> 16 + ); + + if (version == fwhdr->version) { + /* No download, firmware already on board */ + ret = 0; + goto out; + } + + if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { + if (updatebootloader) { + /* Get ready to upload the bootloader */ + bootloadersize = (boothdr->firmwaresize + + boothdr->bslsize) * 16 + + sizeof(struct fw_header); + + bootloaderoffset = (u8 *)(fw->data + + sizeof(struct fw_header)); + + dprintk(DBGLVL_FW, "bootloader d/l starts.\n"); + printk(KERN_INFO "%s() FirmwareSize = 0x%x\n", + __func__, boothdr->firmwaresize); + printk(KERN_INFO "%s() BSLSize = 0x%x\n", + __func__, boothdr->bslsize); + printk(KERN_INFO "%s() Reserved = 0x%x\n", + __func__, boothdr->reserved); + printk(KERN_INFO "%s() Version = 0x%x\n", + __func__, boothdr->version); + ret = saa7164_downloadimage( + dev, + bootloaderoffset, + bootloadersize, + SAA_DOWNLOAD_FLAGS, + dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, + SAA_DEVICE_BUFFERBLOCKSIZE); + if (ret < 0) { + printk(KERN_ERR + "bootloader d/l has failed\n"); + goto out; + } + dprintk(DBGLVL_FW, + "bootloader download complete.\n"); + + } + + printk(KERN_ERR "starting firmware download(2)\n"); + bootloadersize = (boothdr->firmwaresize + + boothdr->bslsize) * 16 + + sizeof(struct fw_header); + + bootloaderoffset = + (u8 *)(fw->data + sizeof(struct fw_header)); + + fwloaderoffset = bootloaderoffset + bootloadersize; + + /* TODO: fix this bounds overrun here with old f/ws */ + fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) * + 16 + sizeof(struct fw_header); + + ret = saa7164_downloadimage( + dev, + fwloaderoffset, + fwloadersize, + SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET, + dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET, + SAA_DEVICE_2ND_BUFFERBLOCKSIZE); + if (ret < 0) { + printk(KERN_ERR "firmware download failed\n"); + goto out; + } + printk(KERN_ERR "firmware download complete.\n"); + + } else { + + /* No bootloader update reqd, download firmware only */ + printk(KERN_ERR "starting firmware download(3)\n"); + + ret = saa7164_downloadimage( + dev, + (u8 *)fw->data, + fw->size, + SAA_DOWNLOAD_FLAGS, + dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, + SAA_DEVICE_BUFFERBLOCKSIZE); + if (ret < 0) { + printk(KERN_ERR "firmware download failed\n"); + goto out; + } + printk(KERN_ERR "firmware download complete.\n"); + } + } + + ret = 0; + +out: + if (fw) + release_firmware(fw); + + return ret; +} diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c new file mode 100644 index 00000000000..e1ae9b01bf0 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-i2c.c @@ -0,0 +1,141 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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/init.h> +#include <linux/delay.h> +#include <asm/io.h> + +#include "saa7164.h" + +static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct saa7164_i2c *bus = i2c_adap->algo_data; + struct saa7164_dev *dev = bus->dev; + int i, retval = 0; + + dprintk(DBGLVL_I2C, "%s(num = %d)\n", __func__, num); + + for (i = 0 ; i < num; i++) { + dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* Unsupported - Yet*/ + printk(KERN_ERR "%s() Unsupported - Yet\n", __func__); + continue; + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + + retval = saa7164_api_i2c_read(bus, msgs[i].addr, + msgs[i].len, msgs[i].buf, + msgs[i+1].len, msgs[i+1].buf + ); + + i++; + + if (retval < 0) + goto err; + } else { + /* write */ + retval = saa7164_api_i2c_write(bus, msgs[i].addr, + msgs[i].len, msgs[i].buf); + } + if (retval < 0) + goto err; + } + return num; + + err: + return retval; +} + +void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd, + void *arg) +{ + if (bus->i2c_rc != 0) + return; + + i2c_clients_command(&bus->i2c_adap, cmd, arg); +} + +static u32 saa7164_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm saa7164_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = saa7164_functionality, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter saa7164_i2c_adap_template = { + .name = "saa7164", + .owner = THIS_MODULE, + .algo = &saa7164_i2c_algo_template, +}; + +static struct i2c_client saa7164_i2c_client_template = { + .name = "saa7164 internal", +}; + +int saa7164_i2c_register(struct saa7164_i2c *bus) +{ + struct saa7164_dev *dev = bus->dev; + + dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr); + + memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template, + sizeof(bus->i2c_adap)); + + memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template, + sizeof(bus->i2c_algo)); + + memcpy(&bus->i2c_client, &saa7164_i2c_client_template, + sizeof(bus->i2c_client)); + + bus->i2c_adap.dev.parent = &dev->pci->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, + sizeof(bus->i2c_adap.name)); + + bus->i2c_algo.data = bus; + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, bus); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + if (0 != bus->i2c_rc) + printk(KERN_ERR "%s: i2c bus %d register FAILED\n", + dev->name, bus->nr); + + return bus->i2c_rc; +} + +int saa7164_i2c_unregister(struct saa7164_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/video/saa7164/saa7164-reg.h new file mode 100644 index 00000000000..06be4c13d5b --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-reg.h @@ -0,0 +1,166 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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. + */ + +/* TODO: Retest the driver with errors expressed as negatives */ + +/* Result codes */ +#define SAA_OK 0 +#define SAA_ERR_BAD_PARAMETER 0x09 +#define SAA_ERR_NO_RESOURCES 0x0c +#define SAA_ERR_NOT_SUPPORTED 0x13 +#define SAA_ERR_BUSY 0x15 +#define SAA_ERR_READ 0x17 +#define SAA_ERR_TIMEOUT 0x1f +#define SAA_ERR_OVERFLOW 0x20 +#define SAA_ERR_EMPTY 0x22 +#define SAA_ERR_NOT_STARTED 0x23 +#define SAA_ERR_ALREADY_STARTED 0x24 +#define SAA_ERR_NOT_STOPPED 0x25 +#define SAA_ERR_ALREADY_STOPPED 0x26 +#define SAA_ERR_INVALID_COMMAND 0x3e +#define SAA_ERR_NULL_PACKET 0x59 + +/* Errors and flags from the silicon */ +#define PVC_ERRORCODE_UNKNOWN 0x00 +#define PVC_ERRORCODE_INVALID_COMMAND 0x01 +#define PVC_ERRORCODE_INVALID_CONTROL 0x02 +#define PVC_ERRORCODE_INVALID_DATA 0x03 +#define PVC_ERRORCODE_TIMEOUT 0x04 +#define PVC_ERRORCODE_NAK 0x05 +#define PVC_RESPONSEFLAG_ERROR 0x01 +#define PVC_RESPONSEFLAG_OVERFLOW 0x02 +#define PVC_RESPONSEFLAG_RESET 0x04 +#define PVC_RESPONSEFLAG_INTERFACE 0x08 +#define PVC_RESPONSEFLAG_CONTINUED 0x10 +#define PVC_CMDFLAG_INTERRUPT 0x02 +#define PVC_CMDFLAG_INTERFACE 0x04 +#define PVC_CMDFLAG_SERIALIZE 0x08 +#define PVC_CMDFLAG_CONTINUE 0x10 + +/* Silicon Commands */ +#define GET_DESCRIPTORS_CONTROL 0x01 +#define GET_STRING_CONTROL 0x03 +#define GET_LANGUAGE_CONTROL 0x05 +#define SET_POWER_CONTROL 0x07 +#define GET_FW_VERSION_CONTROL 0x09 +#define SET_DEBUG_LEVEL_CONTROL 0x0B +#define GET_DEBUG_DATA_CONTROL 0x0C +#define GET_PRODUCTION_INFO_CONTROL 0x0D + +/* cmd defines */ +#define SAA_CMDFLAG_CONTINUE 0x10 +#define SAA_CMD_MAX_MSG_UNITS 256 + +/* Some defines */ +#define SAA_BUS_TIMEOUT 50 +#define SAA_DEVICE_TIMEOUT 5000 +#define SAA_DEVICE_MAXREQUESTSIZE 256 + +/* Register addresses */ +#define SAA_DEVICE_VERSION 0x30 +#define SAA_DOWNLOAD_FLAGS 0x34 +#define SAA_DOWNLOAD_FLAG 0x34 +#define SAA_DOWNLOAD_FLAG_ACK 0x38 +#define SAA_DATAREADY_FLAG 0x3C +#define SAA_DATAREADY_FLAG_ACK 0x40 + +/* Boot loader register and bit definitions */ +#define SAA_BOOTLOADERERROR_FLAGS 0x44 +#define SAA_DEVICE_IMAGE_SEARCHING 0x01 +#define SAA_DEVICE_IMAGE_LOADING 0x02 +#define SAA_DEVICE_IMAGE_BOOTING 0x03 +#define SAA_DEVICE_IMAGE_CORRUPT 0x04 +#define SAA_DEVICE_MEMORY_CORRUPT 0x08 +#define SAA_DEVICE_NO_IMAGE 0x10 + +/* Register addresses */ +#define SAA_DEVICE_2ND_VERSION 0x50 +#define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET 0x54 + +/* Register addresses */ +#define SAA_SECONDSTAGEERROR_FLAGS 0x64 + +/* Bootloader regs and flags */ +#define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET 0x6C +#define SAA_DEVICE_DEADLOCK_DETECTED 0xDEADDEAD + +/* Basic firmware status registers */ +#define SAA_DEVICE_SYSINIT_STATUS_OFFSET 0x70 +#define SAA_DEVICE_SYSINIT_STATUS 0x70 +#define SAA_DEVICE_SYSINIT_MODE 0x74 +#define SAA_DEVICE_SYSINIT_SPEC 0x78 +#define SAA_DEVICE_SYSINIT_INST 0x7C +#define SAA_DEVICE_SYSINIT_CPULOAD 0x80 +#define SAA_DEVICE_SYSINIT_REMAINHEAP 0x84 + +#define SAA_DEVICE_DOWNLOAD_OFFSET 0x1000 +#define SAA_DEVICE_BUFFERBLOCKSIZE 0x1000 + +#define SAA_DEVICE_2ND_BUFFERBLOCKSIZE 0x100000 +#define SAA_DEVICE_2ND_DOWNLOAD_OFFSET 0x200000 + +/* Descriptors */ +#define CS_INTERFACE 0x24 + +/* Descriptor subtypes */ +#define VC_INPUT_TERMINAL 0x02 +#define VC_OUTPUT_TERMINAL 0x03 +#define VC_SELECTOR_UNIT 0x04 +#define VC_PROCESSING_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define TUNER_UNIT 0x09 +#define ENCODER_UNIT 0x0A +#define EXTENSION_UNIT 0x0B +#define VC_TUNER_PATH 0xF0 +#define PVC_HARDWARE_DESCRIPTOR 0xF1 +#define PVC_INTERFACE_DESCRIPTOR 0xF2 +#define PVC_INFRARED_UNIT 0xF3 +#define DRM_UNIT 0xF4 +#define GENERAL_REQUEST 0xF5 + +/* Format Types */ +#define VS_FORMAT_TYPE 0x02 +#define VS_FORMAT_TYPE_I 0x01 +#define VS_FORMAT_UNCOMPRESSED 0x04 +#define VS_FRAME_UNCOMPRESSED 0x05 +#define VS_FORMAT_MPEG2PS 0x09 +#define VS_FORMAT_MPEG2TS 0x0A +#define VS_FORMAT_MPEG4SL 0x0B +#define VS_FORMAT_WM9 0x0C +#define VS_FORMAT_DIVX 0x0D +#define VS_FORMAT_VBI 0x0E +#define VS_FORMAT_RDS 0x0F + +/* Device extension commands */ +#define EXU_REGISTER_ACCESS_CONTROL 0x00 +#define EXU_GPIO_CONTROL 0x01 +#define EXU_GPIO_GROUP_CONTROL 0x02 +#define EXU_INTERRUPT_CONTROL 0x03 + +/* State Transition and args */ +#define SAA_STATE_CONTROL 0x03 +#define SAA_DMASTATE_STOP 0x00 +#define SAA_DMASTATE_ACQUIRE 0x01 +#define SAA_DMASTATE_PAUSE 0x02 +#define SAA_DMASTATE_RUN 0x03 + +/* Hardware registers */ + diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/video/saa7164/saa7164-types.h new file mode 100644 index 00000000000..99093f23aae --- /dev/null +++ b/drivers/media/video/saa7164/saa7164-types.h @@ -0,0 +1,287 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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. + */ + +/* TODO: Cleanup and shorten the namespace */ + +/* Some structues are passed directly to/from the firmware and + * have strict alignment requirements. This is one of them. + */ +typedef struct { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u16 bcdSpecVersion; + u32 dwClockFrequency; + u32 dwClockUpdateRes; + u8 bCapabilities; + u32 dwDeviceRegistersLocation; + u32 dwHostMemoryRegion; + u32 dwHostMemoryRegionSize; + u32 dwHostHibernatMemRegion; + u32 dwHostHibernatMemRegionSize; +} __attribute__((packed)) tmComResHWDescr_t; + +/* This is DWORD aligned on windows but I can't find the right + * gcc syntax to match the binary data from the device. + * I've manually padded with Reserved[3] bytes to match the hardware, + * but this could break if GCC decies to pack in a different way. + */ +typedef struct { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubtype; + u8 bFlags; + u8 bInterfaceType; + u8 bInterfaceId; + u8 bBaseInterface; + u8 bInterruptId; + u8 bDebugInterruptId; + u8 BARLocation; + u8 Reserved[3]; +} tmComResInterfaceDescr_t; + +typedef struct { + u64 CommandRing; + u64 ResponseRing; + u32 CommandWrite; + u32 CommandRead; + u32 ResponseWrite; + u32 ResponseRead; +} tmComResBusDescr_t; + +typedef enum { + NONE = 0, + TYPE_BUS_PCI = 1, + TYPE_BUS_PCIe = 2, + TYPE_BUS_USB = 3, + TYPE_BUS_I2C = 4 +} tmBusType_t; + +typedef struct { + tmBusType_t Type; + u16 m_wMaxReqSize; + u8 *m_pdwSetRing; + u32 m_dwSizeSetRing; + u8 *m_pdwGetRing; + u32 m_dwSizeGetRing; + u32 *m_pdwSetWritePos; + u32 *m_pdwSetReadPos; + u32 *m_pdwGetWritePos; + u32 *m_pdwGetReadPos; + + /* All access is protected */ + struct mutex lock; + +} tmComResBusInfo_t; + +typedef struct { + u8 id; + u8 flags; + u16 size; + u32 command; + u16 controlselector; + u8 seqno; +} __attribute__((packed)) tmComResInfo_t; + +typedef enum { + SET_CUR = 0x01, + GET_CUR = 0x81, + GET_MIN = 0x82, + GET_MAX = 0x83, + GET_RES = 0x84, + GET_LEN = 0x85, + GET_INFO = 0x86, + GET_DEF = 0x87 +} tmComResCmd_t; + +struct cmd { + u8 seqno; + u32 inuse; + u32 timeout; + u32 signalled; + struct mutex lock; + wait_queue_head_t wait; +}; + +typedef struct { + u32 pathid; + u32 size; + void *descriptor; +} tmDescriptor_t; + +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 unitid; +} __attribute__((packed)) tmComResDescrHeader_t; + +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 unitid; + u32 devicetype; + u16 deviceid; + u32 numgpiopins; + u8 numgpiogroups; + u8 controlsize; +} __attribute__((packed)) tmComResExtDevDescrHeader_t; + +typedef struct { + u32 pin; + u8 state; +} __attribute__((packed)) tmComResGPIO_t; + +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 pathid; +} __attribute__((packed)) tmComResPathDescrHeader_t; + +/* terminaltype */ +typedef enum { + ITT_ANTENNA = 0x0203, + LINE_CONNECTOR = 0x0603, + SPDIF_CONNECTOR = 0x0605, + COMPOSITE_CONNECTOR = 0x0401, + SVIDEO_CONNECTOR = 0x0402, + COMPONENT_CONNECTOR = 0x0403, + STANDARD_DMA = 0xF101 +} tmComResTermType_t; + +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 terminalid; + u16 terminaltype; + u8 assocterminal; + u8 iterminal; + u8 controlsize; +} __attribute__((packed)) tmComResAntTermDescrHeader_t; + +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 unitid; + u8 sourceid; + u8 iunit; + u32 tuningstandards; + u8 controlsize; + u32 controls; +} __attribute__((packed)) tmComResTunerDescrHeader_t; + +typedef enum { + /* the buffer does not contain any valid data */ + TM_BUFFER_FLAG_EMPTY, + + /* the buffer is filled with valid data */ + TM_BUFFER_FLAG_DONE, + + /* the buffer is the dummy buffer - TODO??? */ + TM_BUFFER_FLAG_DUMMY_BUFFER +} tmBufferFlag_t; + +typedef struct { + u64 *pagetablevirt; + u64 pagetablephys; + u16 offset; + u8 *context; + u64 timestamp; + tmBufferFlag_t BufferFlag_t; + u32 lostbuffers; + u32 validbuffers; + u64 *dummypagevirt; + u64 dummypagephys; + u64 *addressvirt; +} tmBuffer_t; + +typedef struct { + u32 bitspersample; + u32 samplesperline; + u32 numberoflines; + u32 pitch; + u32 linethreshold; + u64 **pagetablelistvirt; + u64 *pagetablelistphys; + u32 numpagetables; + u32 numpagetableentries; +} tmHWStreamParameters_t; + +typedef struct { + tmHWStreamParameters_t HWStreamParameters_t; + u64 qwDummyPageTablePhys; + u64 *pDummyPageTableVirt; +} tmStreamParameters_t; + +typedef struct { + u8 len; + u8 type; + u8 subtyle; + u8 unitid; + u16 terminaltype; + u8 assocterminal; + u8 sourceid; + u8 iterminal; + u32 BARLocation; + u8 flags; + u8 interruptid; + u8 buffercount; + u8 metadatasize; + u8 numformats; + u8 controlsize; +} __attribute__((packed)) tmComResDMATermDescrHeader_t; + +/* + * + * Description: + * This is the transport stream format header. + * + * Settings: + * bLength - The size of this descriptor in bytes. + * bDescriptorType - CS_INTERFACE. + * bDescriptorSubtype - VS_FORMAT_MPEG2TS descriptor subtype. + * bFormatIndex - A non-zero constant that uniquely identifies the + * format. + * bDataOffset - Offset to TSP packet within MPEG-2 TS transport + * stride, in bytes. + * bPacketLength - Length of TSP packet, in bytes (typically 188). + * bStrideLength - Length of MPEG-2 TS transport stride. + * guidStrideFormat - A Globally Unique Identifier indicating the + * format of the stride data (if any). Set to zeros + * if there is no Stride Data, or if the Stride + * Data is to be ignored by the application. + * + */ +typedef struct { + u8 len; + u8 type; + u8 subtype; + u8 bFormatIndex; + u8 bDataOffset; + u8 bPacketLength; + u8 bStrideLength; + u8 guidStrideFormat[16]; +} __attribute__((packed)) tmComResTSFormatDescrHeader_t; + diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h new file mode 100644 index 00000000000..42660b546f0 --- /dev/null +++ b/drivers/media/video/saa7164/saa7164.h @@ -0,0 +1,400 @@ +/* + * Driver for the NXP SAA7164 PCIe bridge + * + * Copyright (c) 2009 Steven Toth <stoth@kernellabs.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. + */ + +/* + Driver architecture + ******************* + + saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c + | : Standard Linux driver framework for creating + | : exposing and managing interfaces to the rest + | : of the kernel or userland. Also uses _fw.c to load + | : firmware direct into the PCIe bus, bypassing layers. + V + saa7164_api..() : Translate kernel specific functions/features + | : into command buffers. + V + saa7164_cmd..() : Manages the flow of command packets on/off, + | : the bus. Deal with bus errors, timeouts etc. + V + saa7164_bus..() : Manage a read/write memory ring buffer in the + | : PCIe Address space. + | + | saa7164_fw...() : Load any frimware + | | : direct into the device + V V + <- ----------------- PCIe address space -------------------- -> +*/ + +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/kdev_t.h> + +#include <media/tuner.h> +#include <media/tveeprom.h> +#include <media/videobuf-dma-sg.h> +#include <media/videobuf-dvb.h> + +#include "saa7164-reg.h" +#include "saa7164-types.h" + +#include <linux/version.h> +#include <linux/mutex.h> + +#define SAA7164_MAXBOARDS 8 + +#define UNSET (-1U) +#define SAA7164_BOARD_NOAUTO UNSET +#define SAA7164_BOARD_UNKNOWN 0 +#define SAA7164_BOARD_UNKNOWN_REV2 1 +#define SAA7164_BOARD_UNKNOWN_REV3 2 +#define SAA7164_BOARD_HAUPPAUGE_HVR2250 3 +#define SAA7164_BOARD_HAUPPAUGE_HVR2200 4 +#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5 +#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6 +#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7 +#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 + +#define SAA7164_MAX_UNITS 8 +#define SAA7164_TS_NUMBER_OF_LINES 312 +#define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */ + +#define DBGLVL_FW 4 +#define DBGLVL_DVB 8 +#define DBGLVL_I2C 16 +#define DBGLVL_API 32 +#define DBGLVL_CMD 64 +#define DBGLVL_BUS 128 +#define DBGLVL_IRQ 256 +#define DBGLVL_BUF 512 + +enum port_t { + SAA7164_MPEG_UNDEFINED = 0, + SAA7164_MPEG_DVB, +}; + +enum saa7164_i2c_bus_nr { + SAA7164_I2C_BUS_0 = 0, + SAA7164_I2C_BUS_1, + SAA7164_I2C_BUS_2, +}; + +enum saa7164_buffer_flags { + SAA7164_BUFFER_UNDEFINED = 0, + SAA7164_BUFFER_FREE, + SAA7164_BUFFER_BUSY, + SAA7164_BUFFER_FULL +}; + +enum saa7164_unit_type { + SAA7164_UNIT_UNDEFINED = 0, + SAA7164_UNIT_DIGITAL_DEMODULATOR, + SAA7164_UNIT_ANALOG_DEMODULATOR, + SAA7164_UNIT_TUNER, + SAA7164_UNIT_EEPROM, + SAA7164_UNIT_ZILOG_IRBLASTER, + SAA7164_UNIT_ENCODER, +}; + +/* The PCIe bridge doesn't grant direct access to i2c. + * Instead, you address i2c devices using a uniqely + * allocated 'unitid' value via a messaging API. This + * is a problem. The kernel and existing demod/tuner + * drivers expect to talk 'i2c', so we have to maintain + * a translation layer, and a series of functions to + * convert i2c bus + device address into a unit id. + */ +struct saa7164_unit { + enum saa7164_unit_type type; + u8 id; + char *name; + enum saa7164_i2c_bus_nr i2c_bus_nr; + u8 i2c_bus_addr; + u8 i2c_reg_len; +}; + +struct saa7164_board { + char *name; + enum port_t porta, portb; + enum { + SAA7164_CHIP_UNDEFINED = 0, + SAA7164_CHIP_REV2, + SAA7164_CHIP_REV3, + } chiprev; + struct saa7164_unit unit[SAA7164_MAX_UNITS]; +}; + +struct saa7164_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +struct saa7164_fw_status { + + /* RISC Core details */ + u32 status; + u32 mode; + u32 spec; + u32 inst; + u32 cpuload; + u32 remainheap; + + /* Firmware version */ + u32 version; + u32 major; + u32 sub; + u32 rel; + u32 buildnr; +}; + +struct saa7164_dvb { + struct mutex lock; + struct dvb_adapter adapter; + struct dvb_frontend *frontend; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + int feeding; +}; + +struct saa7164_i2c { + struct saa7164_dev *dev; + + enum saa7164_i2c_bus_nr nr; + + /* I2C I/O */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; +}; + +struct saa7164_tsport; + +struct saa7164_buffer { + struct list_head list; + + u32 nr; + + struct saa7164_tsport *port; + + /* Hardware Specific */ + /* PCI Memory allocations */ + enum saa7164_buffer_flags flags; /* Free, Busy, Full */ + + /* A block of page align PCI memory */ + u32 pci_size; /* PCI allocation size in bytes */ + u64 *cpu; /* Virtual address */ + dma_addr_t dma; /* Physical address */ + + /* A page table that splits the block into a number of entries */ + u32 pt_size; /* PCI allocation size in bytes */ + u64 *pt_cpu; /* Virtual address */ + dma_addr_t pt_dma; /* Physical address */ +}; + +struct saa7164_tsport { + + struct saa7164_dev *dev; + int nr; + enum port_t type; + + struct saa7164_dvb dvb; + + /* HW related stream parameters */ + tmHWStreamParameters_t hw_streamingparams; + + /* DMA configuration values, is seeded during initialization */ + tmComResDMATermDescrHeader_t hwcfg; + + /* hardware specific registers */ + u32 bufcounter; + u32 pitch; + u32 bufsize; + u32 bufoffset; + u32 bufptr32l; + u32 bufptr32h; + u64 bufptr64; + + u32 numpte; /* Number of entries in array, only valid in head */ + struct mutex dmaqueue_lock; + struct mutex dummy_dmaqueue_lock; + struct saa7164_buffer dmaqueue; + struct saa7164_buffer dummy_dmaqueue; + +}; + +struct saa7164_dev { + struct list_head devlist; + atomic_t refcount; + + /* pci stuff */ + struct pci_dev *pci; + unsigned char pci_rev, pci_lat; + int pci_bus, pci_slot; + u32 __iomem *lmmio; + u8 __iomem *bmmio; + u32 __iomem *lmmio2; + u8 __iomem *bmmio2; + int pci_irqmask; + + /* board details */ + int nr; + int hwrevision; + u32 board; + char name[32]; + + /* firmware status */ + struct saa7164_fw_status fw_status; + + tmComResHWDescr_t hwdesc; + tmComResInterfaceDescr_t intfdesc; + tmComResBusDescr_t busdesc; + + tmComResBusInfo_t bus; + + /* Interrupt status and ack registers */ + u32 int_status; + u32 int_ack; + + struct cmd cmds[SAA_CMD_MAX_MSG_UNITS]; + struct mutex lock; + + /* I2c related */ + struct saa7164_i2c i2c_bus[3]; + + /* Transport related */ + struct saa7164_tsport ts1, ts2; + + /* Deferred command/api interrupts handling */ + struct work_struct workcmd; + +}; + +extern struct list_head saa7164_devlist; +extern unsigned int waitsecs; + +/* ----------------------------------------------------------- */ +/* saa7164-core.c */ +void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr); +void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len); +void saa7164_getfirmwarestatus(struct saa7164_dev *dev); +u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7164-fw.c */ +int saa7164_downloadfirmware(struct saa7164_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7164-i2c.c */ +extern int saa7164_i2c_register(struct saa7164_i2c *bus); +extern int saa7164_i2c_unregister(struct saa7164_i2c *bus); +extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus, + unsigned int cmd, void *arg); + +/* ----------------------------------------------------------- */ +/* saa7164-bus.c */ +int saa7164_bus_setup(struct saa7164_dev *dev); +void saa7164_bus_dump(struct saa7164_dev *dev); +int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf); +int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg, + void *buf, int peekonly); + +/* ----------------------------------------------------------- */ +/* saa7164-cmd.c */ +int saa7164_cmd_send(struct saa7164_dev *dev, + u8 id, tmComResCmd_t command, u16 controlselector, + u16 size, void *buf); +void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno); +int saa7164_irq_dequeue(struct saa7164_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7164-api.c */ +int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version); +int saa7164_api_enum_subdevs(struct saa7164_dev *dev); +int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, + u32 datalen, u8 *data); +int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, + u32 datalen, u8 *data); +int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr, + u32 datalen, u8 *data); +int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen); +int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); +int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); +int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode); + +/* ----------------------------------------------------------- */ +/* saa7164-cards.c */ +extern struct saa7164_board saa7164_boards[]; +extern const unsigned int saa7164_bcount; + +extern struct saa7164_subid saa7164_subids[]; +extern const unsigned int saa7164_idcount; + +extern void saa7164_card_list(struct saa7164_dev *dev); +extern void saa7164_gpio_setup(struct saa7164_dev *dev); +extern void saa7164_card_setup(struct saa7164_dev *dev); + +extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr); +extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr); +extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid); + +/* ----------------------------------------------------------- */ +/* saa7164-dvb.c */ +extern int saa7164_dvb_register(struct saa7164_tsport *port); +extern int saa7164_dvb_unregister(struct saa7164_tsport *port); + +/* ----------------------------------------------------------- */ +/* saa7164-buffer.c */ +extern struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port, + u32 len); +extern int saa7164_buffer_dealloc(struct saa7164_tsport *port, + struct saa7164_buffer *buf); + +/* ----------------------------------------------------------- */ + +extern unsigned int saa_debug; +#define dprintk(level, fmt, arg...)\ + do { if (saa_debug & level)\ + printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ + } while (0) + +#define log_warn(fmt, arg...)\ + do { \ + printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ + } while (0) + +#define log_err(fmt, arg...)\ + do { \ + printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\ + } while (0) + +#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) +#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) + + +#define saa7164_readb(reg) readl(dev->bmmio + (reg)) +#define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg)) + diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 0db88a53d92..65ac474c517 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -30,7 +30,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/videodev2.h> -#include <linux/clk.h> +#include <linux/pm_runtime.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -74,6 +74,13 @@ #define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ #define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ +#undef DEBUG_GEOMETRY +#ifdef DEBUG_GEOMETRY +#define dev_geo dev_info +#else +#define dev_geo dev_dbg +#endif + /* per video frame buffer */ struct sh_mobile_ceu_buffer { struct videobuf_buffer vb; /* v4l buffer must be first */ @@ -86,17 +93,27 @@ struct sh_mobile_ceu_dev { unsigned int irq; void __iomem *base; - struct clk *clk; unsigned long video_limit; /* lock used to protect videobuf */ spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; - int is_interlaced; struct sh_mobile_ceu_info *pdata; + u32 cflcr; + + unsigned int is_interlaced:1; + unsigned int image_mode:1; + unsigned int is_16bit:1; +}; + +struct sh_mobile_ceu_cam { + struct v4l2_rect ceu_rect; + unsigned int cam_width; + unsigned int cam_height; + const struct soc_camera_data_format *extra_fmt; const struct soc_camera_data_format *camera_fmt; }; @@ -147,7 +164,8 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, struct sh_mobile_ceu_dev *pcdev = ici->priv; int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3; - *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel); + *size = PAGE_ALIGN(icd->user_width * icd->user_height * + bytes_per_pixel); if (0 == *count) *count = 2; @@ -157,7 +175,7 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, (*count)--; } - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); return 0; } @@ -166,8 +184,9 @@ static void free_buffer(struct videobuf_queue *vq, struct sh_mobile_ceu_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); if (in_interrupt()) @@ -175,7 +194,7 @@ static void free_buffer(struct videobuf_queue *vq, videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_contig_free(vq, &buf->vb); - dev_dbg(&icd->dev, "%s freed\n", __func__); + dev_dbg(dev, "%s freed\n", __func__); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -206,7 +225,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); if (pcdev->is_interlaced) { - phys_addr_bottom = phys_addr_top + icd->width; + phys_addr_bottom = phys_addr_top + icd->user_width; ceu_write(pcdev, CDBYR, phys_addr_bottom); } @@ -215,10 +234,12 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: - phys_addr_top += icd->width * icd->height; + phys_addr_top += icd->user_width * + icd->user_height; ceu_write(pcdev, CDACR, phys_addr_top); if (pcdev->is_interlaced) { - phys_addr_bottom = phys_addr_top + icd->width; + phys_addr_bottom = phys_addr_top + + icd->user_width; ceu_write(pcdev, CDBCR, phys_addr_bottom); } } @@ -237,7 +258,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -252,12 +273,12 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, BUG_ON(NULL == icd->current_fmt); if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -282,32 +303,50 @@ out: return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long flags; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); vb->state = VIDEOBUF_QUEUED; - spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + if (pcdev->active == vb) { + /* disable capture (release DMA buffer), reset */ + ceu_write(pcdev, CAPSR, 1 << 16); + pcdev->active = NULL; + } + + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && + !list_empty(&vb->queue)) { + vb->state = VIDEOBUF_ERROR; + list_del_init(&vb->queue); + } + + spin_unlock_irqrestore(&pcdev->lock, flags); + free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); } @@ -327,6 +366,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) spin_lock_irqsave(&pcdev->lock, flags); vb = pcdev->active; + if (!vb) + /* Stale interrupt from a released buffer */ + goto out; + list_del_init(&vb->queue); if (!list_empty(&pcdev->capture)) @@ -341,6 +384,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); + +out: spin_unlock_irqrestore(&pcdev->lock, flags); return IRQ_HANDLED; @@ -351,28 +396,23 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret = -EBUSY; if (pcdev->icd) - goto err; + return -EBUSY; - dev_info(&icd->dev, + dev_info(icd->dev.parent, "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); - ret = icd->ops->init(icd); - if (ret) - goto err; - - clk_enable(pcdev->clk); + pm_runtime_get_sync(ici->v4l2_dev.dev); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ while (ceu_read(pcdev, CSTSR) & 1) msleep(1); pcdev->icd = icd; -err: - return ret; + + return 0; } /* Called with .video_lock held */ @@ -398,25 +438,151 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irqrestore(&pcdev->lock, flags); - clk_disable(pcdev->clk); - - icd->ops->release(icd); + pm_runtime_put_sync(ici->v4l2_dev.dev); - dev_info(&icd->dev, + dev_info(icd->dev.parent, "SuperH Mobile CEU driver detached from camera %d\n", icd->devnum); pcdev->icd = NULL; } +/* + * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" + * in SH7722 Hardware Manual + */ +static unsigned int size_dst(unsigned int src, unsigned int scale) +{ + unsigned int mant_pre = scale >> 12; + if (!src || !scale) + return src; + return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * + mant_pre * 4096 / scale + 1; +} + +static u16 calc_scale(unsigned int src, unsigned int *dst) +{ + u16 scale; + + if (src == *dst) + return 0; + + scale = (src * 4096 / *dst) & ~7; + + while (scale > 4096 && size_dst(src, scale) < *dst) + scale -= 8; + + *dst = size_dst(src, scale); + + return scale; +} + +/* rect is guaranteed to not exceed the scaled camera rectangle */ +static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, + unsigned int out_width, + unsigned int out_height) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *rect = &cam->ceu_rect; + struct sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned int height, width, cdwdr_width, in_width, in_height; + unsigned int left_offset, top_offset; + u32 camor; + + dev_dbg(icd->dev.parent, "Crop %ux%u@%u:%u\n", + rect->width, rect->height, rect->left, rect->top); + + left_offset = rect->left; + top_offset = rect->top; + + if (pcdev->image_mode) { + in_width = rect->width; + if (!pcdev->is_16bit) { + in_width *= 2; + left_offset *= 2; + } + width = cdwdr_width = out_width; + } else { + unsigned int w_factor = (icd->current_fmt->depth + 7) >> 3; + + width = out_width * w_factor / 2; + + if (!pcdev->is_16bit) + w_factor *= 2; + + in_width = rect->width * w_factor / 2; + left_offset = left_offset * w_factor / 2; + + cdwdr_width = width * 2; + } + + height = out_height; + in_height = rect->height; + if (pcdev->is_interlaced) { + height /= 2; + in_height /= 2; + top_offset /= 2; + cdwdr_width *= 2; + } + + /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ + camor = left_offset | (top_offset << 16); + + dev_geo(icd->dev.parent, + "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, + (in_height << 16) | in_width, (height << 16) | width, + cdwdr_width); + + ceu_write(pcdev, CAMOR, camor); + ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); + ceu_write(pcdev, CFSZR, (height << 16) | width); + ceu_write(pcdev, CDWDR, cdwdr_width); +} + +static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev) +{ + u32 capsr = ceu_read(pcdev, CAPSR); + ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */ + return capsr; +} + +static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) +{ + unsigned long timeout = jiffies + 10 * HZ; + + /* + * Wait until the end of the current frame. It can take a long time, + * but if it has been aborted by a CAPSR reset, it shoule exit sooner. + */ + while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout)) + msleep(1); + + if (time_after(jiffies, timeout)) { + dev_err(pcdev->ici.v4l2_dev.dev, + "Timeout waiting for frame end! Interface problem?\n"); + return; + } + + /* Wait until reset clears, this shall not hang... */ + while (ceu_read(pcdev, CAPSR) & (1 << 16)) + udelay(10); + + /* Anything to restore? */ + if (capsr & ~(1 << 16)) + ceu_write(pcdev, CAPSR, capsr); +} + static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret, buswidth, width, height, cfszr_width, cdwdr_width; + int ret; unsigned long camera_flags, common_flags, value; - int yuv_mode, yuv_lineskip; + int yuv_lineskip; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + u32 capsr = capture_save_reset(pcdev); camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, @@ -430,10 +596,10 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, switch (common_flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_8: - buswidth = 8; + pcdev->is_16bit = 0; break; case SOCAM_DATAWIDTH_16: - buswidth = 16; + pcdev->is_16bit = 1; break; default: return -EINVAL; @@ -443,7 +609,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CRCMPR, 0); value = 0x00000010; /* data fetch by default */ - yuv_mode = yuv_lineskip = 0; + yuv_lineskip = 0; switch (icd->current_fmt->fourcc) { case V4L2_PIX_FMT_NV12: @@ -452,8 +618,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, /* fall-through */ case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: - yuv_mode = 1; - switch (pcdev->camera_fmt->fourcc) { + switch (cam->camera_fmt->fourcc) { case V4L2_PIX_FMT_UYVY: value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ break; @@ -477,36 +642,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - value |= buswidth == 16 ? 1 << 12 : 0; + value |= pcdev->is_16bit ? 1 << 12 : 0; ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0); + sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height); mdelay(1); - if (yuv_mode) { - width = icd->width * 2; - width = buswidth == 16 ? width / 2 : width; - cfszr_width = cdwdr_width = icd->width; - } else { - width = icd->width * ((icd->current_fmt->depth + 7) >> 3); - width = buswidth == 16 ? width / 2 : width; - cfszr_width = buswidth == 8 ? width / 2 : width; - cdwdr_width = buswidth == 16 ? width * 2 : width; - } - - height = icd->height; - if (pcdev->is_interlaced) { - height /= 2; - cdwdr_width *= 2; - } - - ceu_write(pcdev, CAMOR, 0); - ceu_write(pcdev, CAPWR, (height << 16) | width); - ceu_write(pcdev, CFLCR, 0); /* no scaling */ - ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); - ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */ + ceu_write(pcdev, CFLCR, pcdev->cflcr); /* A few words about byte order (observed in Big Endian mode) * @@ -525,10 +670,15 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */ ceu_write(pcdev, CDOCR, value); - - ceu_write(pcdev, CDWDR, cdwdr_width); ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ + dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n", + pixfmt & 0xff, (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, + icd->user_width, icd->user_height); + + capture_restore(pcdev, capsr); + /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ return 0; } @@ -578,24 +728,35 @@ static const struct soc_camera_data_format sh_mobile_ceu_formats[] = { static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->dev.parent; int ret, k, n; int formats = 0; + struct sh_mobile_ceu_cam *cam; ret = sh_mobile_ceu_try_bus_param(icd); if (ret < 0) return 0; + if (!icd->host_priv) { + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + /* Beginning of a pass */ if (!idx) - icd->host_priv = NULL; + cam->extra_fmt = NULL; switch (icd->formats[idx].fourcc) { case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: - if (icd->host_priv) + if (cam->extra_fmt) goto add_single_format; /* @@ -607,7 +768,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, * the host_priv pointer and check whether the format you're * going to add now is already there. */ - icd->host_priv = (void *)sh_mobile_ceu_formats; + cam->extra_fmt = (void *)sh_mobile_ceu_formats; n = ARRAY_SIZE(sh_mobile_ceu_formats); formats += n; @@ -616,7 +777,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(dev, "Providing format %s using %s\n", sh_mobile_ceu_formats[k].name, icd->formats[idx].name); } @@ -629,7 +790,7 @@ add_single_format: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, + dev_dbg(dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -638,82 +799,714 @@ add_single_format: return formats; } +static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +/* Check if any dimension of r1 is smaller than respective one of r2 */ +static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) +{ + return r1->width < r2->width || r1->height < r2->height; +} + +/* Check if r1 fails to cover r2 */ +static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2) +{ + return r1->left > r2->left || r1->top > r2->top || + r1->left + r1->width < r2->left + r2->width || + r1->top + r1->height < r2->top + r2->height; +} + +static unsigned int scale_down(unsigned int size, unsigned int scale) +{ + return (size * 4096 + scale / 2) / scale; +} + +static unsigned int scale_up(unsigned int size, unsigned int scale) +{ + return (size * scale + 2048) / 4096; +} + +static unsigned int calc_generic_scale(unsigned int input, unsigned int output) +{ + return (input * 4096 + output / 2) / output; +} + +static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) +{ + struct v4l2_crop crop; + struct v4l2_cropcap cap; + int ret; + + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_crop, &crop); + if (!ret) { + *rect = crop.c; + return ret; + } + + /* Camera driver doesn't support .g_crop(), assume default rectangle */ + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + *rect = cap.defrect; + + return ret; +} + +/* + * The common for both scaling and cropping iterative approach is: + * 1. try if the client can produce exactly what requested by the user + * 2. if (1) failed, try to double the client image until we get one big enough + * 3. if (2) failed, try to request the maximum image + */ +static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, + struct v4l2_crop *cam_crop) +{ + struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; + struct device *dev = sd->v4l2_dev->dev; + struct v4l2_cropcap cap; + int ret; + unsigned int width, height; + + v4l2_subdev_call(sd, video, s_crop, crop); + ret = client_g_rect(sd, cam_rect); + if (ret < 0) + return ret; + + /* + * Now cam_crop contains the current camera input rectangle, and it must + * be within camera cropcap bounds + */ + if (!memcmp(rect, cam_rect, sizeof(*rect))) { + /* Even if camera S_CROP failed, but camera rectangle matches */ + dev_dbg(dev, "Camera S_CROP successful for %ux%u@%u:%u\n", + rect->width, rect->height, rect->left, rect->top); + return 0; + } + + /* Try to fix cropping, that camera hasn't managed to set */ + dev_geo(dev, "Fix camera S_CROP for %ux%u@%u:%u to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top, + rect->width, rect->height, rect->left, rect->top); + + /* We need sensor maximum rectangle */ + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, + cap.bounds.width); + soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, + cap.bounds.height); + + /* + * Popular special case - some cameras can only handle fixed sizes like + * QVGA, VGA,... Take care to avoid infinite loop. + */ + width = max(cam_rect->width, 2); + height = max(cam_rect->height, 2); + + while (!ret && (is_smaller(cam_rect, rect) || + is_inside(cam_rect, rect)) && + (cap.bounds.width > width || cap.bounds.height > height)) { + + width *= 2; + height *= 2; + + cam_rect->width = width; + cam_rect->height = height; + + /* + * We do not know what capabilities the camera has to set up + * left and top borders. We could try to be smarter in iterating + * them, e.g., if camera current left is to the right of the + * target left, set it to the middle point between the current + * left and minimum left. But that would add too much + * complexity: we would have to iterate each border separately. + */ + if (cam_rect->left > rect->left) + cam_rect->left = cap.bounds.left; + + if (cam_rect->left + cam_rect->width < rect->left + rect->width) + cam_rect->width = rect->left + rect->width - + cam_rect->left; + + if (cam_rect->top > rect->top) + cam_rect->top = cap.bounds.top; + + if (cam_rect->top + cam_rect->height < rect->top + rect->height) + cam_rect->height = rect->top + rect->height - + cam_rect->top; + + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for %ux%u@%u:%u\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + /* S_CROP must not modify the rectangle */ + if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { + /* + * The camera failed to configure a suitable cropping, + * we cannot use the current rectangle, set to max + */ + *cam_rect = cap.bounds; + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + return ret; +} + +static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect, + unsigned int *scale_h, unsigned int *scale_v) +{ + struct v4l2_format f; + int ret; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + *scale_h = calc_generic_scale(rect->width, f.fmt.pix.width); + *scale_v = calc_generic_scale(rect->height, f.fmt.pix.height); + + return 0; +} + +static int get_camera_subwin(struct soc_camera_device *icd, + struct v4l2_rect *cam_subrect, + unsigned int cam_hscale, unsigned int cam_vscale) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *ceu_rect = &cam->ceu_rect; + + if (!ceu_rect->width) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix; + int ret; + /* First time */ + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + dev_geo(dev, "camera fmt %ux%u\n", pix->width, pix->height); + + if (pix->width > 2560) { + ceu_rect->width = 2560; + ceu_rect->left = (pix->width - 2560) / 2; + } else { + ceu_rect->width = pix->width; + ceu_rect->left = 0; + } + + if (pix->height > 1920) { + ceu_rect->height = 1920; + ceu_rect->top = (pix->height - 1920) / 2; + } else { + ceu_rect->height = pix->height; + ceu_rect->top = 0; + } + + dev_geo(dev, "initialised CEU rect %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + } + + cam_subrect->width = scale_up(ceu_rect->width, cam_hscale); + cam_subrect->left = scale_up(ceu_rect->left, cam_hscale); + cam_subrect->height = scale_up(ceu_rect->height, cam_vscale); + cam_subrect->top = scale_up(ceu_rect->top, cam_vscale); + + return 0; +} + +static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f, + bool ceu_can_scale) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int width = pix->width, height = pix->height, tmp_w, tmp_h; + unsigned int max_width, max_height; + struct v4l2_cropcap cap; + int ret; + + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + max_width = min(cap.bounds.width, 2560); + max_height = min(cap.bounds.height, 1920); + + ret = v4l2_subdev_call(sd, video, s_fmt, f); + if (ret < 0) + return ret; + + dev_geo(dev, "camera scaled to %ux%u\n", pix->width, pix->height); + + if ((width == pix->width && height == pix->height) || !ceu_can_scale) + return 0; + + /* Camera set a format, but geometry is not precise, try to improve */ + tmp_w = pix->width; + tmp_h = pix->height; + + /* width <= max_width && height <= max_height - guaranteed by try_fmt */ + while ((width > tmp_w || height > tmp_h) && + tmp_w < max_width && tmp_h < max_height) { + tmp_w = min(2 * tmp_w, max_width); + tmp_h = min(2 * tmp_h, max_height); + pix->width = tmp_w; + pix->height = tmp_h; + ret = v4l2_subdev_call(sd, video, s_fmt, f); + dev_geo(dev, "Camera scaled to %ux%u\n", + pix->width, pix->height); + if (ret < 0) { + /* This shouldn't happen */ + dev_err(dev, "Client failed to set format: %d\n", ret); + return ret; + } + } + + return 0; +} + +/** + * @rect - camera cropped rectangle + * @sub_rect - CEU cropped rectangle, mapped back to camera input area + * @ceu_rect - on output calculated CEU crop rectangle + */ +static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, + struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect, + struct v4l2_format *f, bool ceu_can_scale) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct device *dev = icd->dev.parent; + struct v4l2_format f_tmp = *f; + struct v4l2_pix_format *pix_tmp = &f_tmp.fmt.pix; + unsigned int scale_h, scale_v; + int ret; + + /* 5. Apply iterative camera S_FMT for camera user window. */ + ret = client_s_fmt(icd, &f_tmp, ceu_can_scale); + if (ret < 0) + return ret; + + dev_geo(dev, "5: camera scaled to %ux%u\n", + pix_tmp->width, pix_tmp->height); + + /* 6. Retrieve camera output window (g_fmt) */ + + /* unneeded - it is already in "f_tmp" */ + + /* 7. Calculate new camera scales. */ + ret = get_camera_scales(sd, rect, &scale_h, &scale_v); + if (ret < 0) + return ret; + + dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v); + + cam->cam_width = pix_tmp->width; + cam->cam_height = pix_tmp->height; + f->fmt.pix.width = pix_tmp->width; + f->fmt.pix.height = pix_tmp->height; + + /* + * 8. Calculate new CEU crop - apply camera scales to previously + * calculated "effective" crop. + */ + ceu_rect->left = scale_down(sub_rect->left, scale_h); + ceu_rect->width = scale_down(sub_rect->width, scale_h); + ceu_rect->top = scale_down(sub_rect->top, scale_v); + ceu_rect->height = scale_down(sub_rect->height, scale_v); + + dev_geo(dev, "8: new CEU rect %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + + return 0; +} + +/* Get combined scales */ +static int get_scales(struct soc_camera_device *icd, + unsigned int *scale_h, unsigned int *scale_v) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_crop cam_crop; + unsigned int width_in, height_in; + int ret; + + cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = client_g_rect(sd, &cam_crop.c); + if (ret < 0) + return ret; + + ret = get_camera_scales(sd, &cam_crop.c, scale_h, scale_v); + if (ret < 0) + return ret; + + width_in = scale_up(cam->ceu_rect.width, *scale_h); + height_in = scale_up(cam->ceu_rect.height, *scale_v); + + *scale_h = calc_generic_scale(cam->ceu_rect.width, icd->user_width); + *scale_v = calc_generic_scale(cam->ceu_rect.height, icd->user_height); + + return 0; +} + +/* + * CEU can scale and crop, but we don't want to waste bandwidth and kill the + * framerate by always requesting the maximum image from the client. See + * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of + * scaling and cropping algorithms and for the meaning of referenced here steps. + */ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { - return icd->ops->set_crop(icd, rect); + struct v4l2_rect *rect = &a->c; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct v4l2_crop cam_crop; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix; + unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v, + out_width, out_height; + u32 capsr, cflcr; + int ret; + + /* 1. Calculate current combined scales. */ + ret = get_scales(icd, &scale_comb_h, &scale_comb_v); + if (ret < 0) + return ret; + + dev_geo(dev, "1: combined scales %u:%u\n", scale_comb_h, scale_comb_v); + + /* 2. Apply iterative camera S_CROP for new input window. */ + ret = client_s_crop(sd, a, &cam_crop); + if (ret < 0) + return ret; + + dev_geo(dev, "2: camera cropped to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + + /* On success cam_crop contains current camera crop */ + + /* + * 3. If old combined scales applied to new crop produce an impossible + * user window, adjust scales to produce nearest possible window. + */ + out_width = scale_down(rect->width, scale_comb_h); + out_height = scale_down(rect->height, scale_comb_v); + + if (out_width > 2560) + out_width = 2560; + else if (out_width < 2) + out_width = 2; + + if (out_height > 1920) + out_height = 1920; + else if (out_height < 4) + out_height = 4; + + dev_geo(dev, "3: Adjusted output %ux%u\n", out_width, out_height); + + /* 4. Use G_CROP to retrieve actual input window: already in cam_crop */ + + /* + * 5. Using actual input window and calculated combined scales calculate + * camera target output window. + */ + pix->width = scale_down(cam_rect->width, scale_comb_h); + pix->height = scale_down(cam_rect->height, scale_comb_v); + + dev_geo(dev, "5: camera target %ux%u\n", pix->width, pix->height); + + /* 6. - 9. */ + pix->pixelformat = cam->camera_fmt->fourcc; + pix->colorspace = cam->camera_fmt->colorspace; + + capsr = capture_save_reset(pcdev); + dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); + + /* Make relative to camera rectangle */ + rect->left -= cam_rect->left; + rect->top -= cam_rect->top; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = client_scale(icd, cam_rect, rect, ceu_rect, &f, + pcdev->image_mode && !pcdev->is_interlaced); + + dev_geo(dev, "6-9: %d\n", ret); + + /* 10. Use CEU cropping to crop to the new window. */ + sh_mobile_ceu_set_rect(icd, out_width, out_height); + + dev_geo(dev, "10: CEU cropped to %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + + /* + * 11. Calculate CEU scales from camera scales from results of (10) and + * user window from (3) + */ + scale_ceu_h = calc_scale(ceu_rect->width, &out_width); + scale_ceu_v = calc_scale(ceu_rect->height, &out_height); + + dev_geo(dev, "11: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + + /* 12. Apply CEU scales. */ + cflcr = scale_ceu_h | (scale_ceu_v << 16); + if (cflcr != pcdev->cflcr) { + pcdev->cflcr = cflcr; + ceu_write(pcdev, CFLCR, cflcr); + } + + /* Restore capture */ + if (pcdev->active) + capsr |= 1; + capture_restore(pcdev, capsr); + + icd->user_width = out_width; + icd->user_height = out_height; + + /* Even if only camera cropping succeeded */ + return ret; } +/* Similar to set_crop multistage iterative algorithm */ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - __u32 pixfmt = f->fmt.pix.pixelformat; - const struct soc_camera_format_xlate *xlate; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_format cam_f = *f; + struct v4l2_pix_format *cam_pix = &cam_f.fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + __u32 pixfmt = pix->pixelformat; + const struct soc_camera_format_xlate *xlate; + struct v4l2_crop cam_crop; + struct v4l2_rect *cam_rect = &cam_crop.c, cam_subrect, ceu_rect; + unsigned int scale_cam_h, scale_cam_v; + u16 scale_v, scale_h; int ret; + bool is_interlaced, image_mode; + + switch (pix->field) { + case V4L2_FIELD_INTERLACED: + is_interlaced = true; + break; + case V4L2_FIELD_ANY: + default: + pix->field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + is_interlaced = false; + break; + } xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(dev, "Format %x not found\n", pixfmt); return -EINVAL; } - cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc; - ret = icd->ops->set_fmt(icd, &cam_f); + /* 1. Calculate current camera scales. */ + cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (!ret) { - icd->buswidth = xlate->buswidth; - icd->current_fmt = xlate->host_fmt; - pcdev->camera_fmt = xlate->cam_fmt; + ret = client_g_rect(sd, cam_rect); + if (ret < 0) + return ret; + + ret = get_camera_scales(sd, cam_rect, &scale_cam_h, &scale_cam_v); + if (ret < 0) + return ret; + + dev_geo(dev, "1: camera scales %u:%u\n", scale_cam_h, scale_cam_v); + + /* + * 2. Calculate "effective" input crop (sensor subwindow) - CEU crop + * scaled back at current camera scales onto input window. + */ + ret = get_camera_subwin(icd, &cam_subrect, scale_cam_h, scale_cam_v); + if (ret < 0) + return ret; + + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", + cam_subrect.width, cam_subrect.height, + cam_subrect.left, cam_subrect.top); + + /* + * 3. Calculate new combined scales from "effective" input window to + * requested user window. + */ + scale_h = calc_generic_scale(cam_subrect.width, pix->width); + scale_v = calc_generic_scale(cam_subrect.height, pix->height); + + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); + + /* + * 4. Calculate camera output window by applying combined scales to real + * input window. + */ + cam_pix->width = scale_down(cam_rect->width, scale_h); + cam_pix->height = scale_down(cam_rect->height, scale_v); + cam_pix->pixelformat = xlate->cam_fmt->fourcc; + + switch (pixfmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + image_mode = true; + break; + default: + image_mode = false; } - return ret; + dev_geo(dev, "4: camera output %ux%u\n", + cam_pix->width, cam_pix->height); + + /* 5. - 9. */ + ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &cam_f, + image_mode && !is_interlaced); + + dev_geo(dev, "5-9: client scale %d\n", ret); + + /* Done with the camera. Now see if we can improve the result */ + + dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + ret, cam_pix->width, cam_pix->height, pix->width, pix->height); + if (ret < 0) + return ret; + + /* 10. Use CEU scaling to scale to the requested user window. */ + + /* We cannot scale up */ + if (pix->width > cam_pix->width) + pix->width = cam_pix->width; + if (pix->width > ceu_rect.width) + pix->width = ceu_rect.width; + + if (pix->height > cam_pix->height) + pix->height = cam_pix->height; + if (pix->height > ceu_rect.height) + pix->height = ceu_rect.height; + + /* Let's rock: scale pix->{width x height} down to width x height */ + scale_h = calc_scale(ceu_rect.width, &pix->width); + scale_v = calc_scale(ceu_rect.height, &pix->height); + + dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", + ceu_rect.width, scale_h, pix->width, + ceu_rect.height, scale_v, pix->height); + + pcdev->cflcr = scale_h | (scale_v << 16); + + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + cam->camera_fmt = xlate->cam_fmt; + cam->ceu_rect = ceu_rect; + + pcdev->is_interlaced = is_interlaced; + pcdev->image_mode = image_mode; + + return 0; } static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; const struct soc_camera_format_xlate *xlate; - __u32 pixfmt = f->fmt.pix.pixelformat; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + __u32 pixfmt = pix->pixelformat; + int width, height; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); return -EINVAL; } /* FIXME: calculate using depth and bus width */ - v4l_bound_align_image(&f->fmt.pix.width, 2, 2560, 1, - &f->fmt.pix.height, 4, 1920, 2, 0); + v4l_bound_align_image(&pix->width, 2, 2560, 1, + &pix->height, 4, 1920, 2, 0); + + width = pix->width; + height = pix->height; - f->fmt.pix.bytesperline = f->fmt.pix.width * + pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + pix->sizeimage = pix->height * pix->bytesperline; + + pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, try_fmt, f); + pix->pixelformat = pixfmt; if (ret < 0) return ret; - switch (f->fmt.pix.field) { - case V4L2_FIELD_INTERLACED: - pcdev->is_interlaced = 1; - break; - case V4L2_FIELD_ANY: - f->fmt.pix.field = V4L2_FIELD_NONE; - /* fall-through */ - case V4L2_FIELD_NONE: - pcdev->is_interlaced = 0; - break; - default: - ret = -EINVAL; - break; + switch (pixfmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + /* FIXME: check against rect_max after converting soc-camera */ + /* We can scale precisely, need a bigger image from camera */ + if (pix->width < width || pix->height < height) { + int tmp_w = pix->width, tmp_h = pix->height; + pix->width = 2560; + pix->height = 1920; + ret = v4l2_subdev_call(sd, video, try_fmt, f); + if (ret < 0) { + /* Shouldn't actually happen... */ + dev_err(icd->dev.parent, + "FIXME: try_fmt() returned %d\n", ret); + pix->width = tmp_w; + pix->height = tmp_h; + } + } + if (pix->width > width) + pix->width = width; + if (pix->height > height) + pix->height = height; } return ret; @@ -773,7 +1566,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &sh_mobile_ceu_videobuf_ops, - ici->dev, &pcdev->lock, + icd->dev.parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, pcdev->is_interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE, @@ -781,27 +1574,80 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, icd); } +static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + u32 val; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + val = ceu_read(pcdev, CLFCR); + ctrl->value = val ^ 1; + return 0; + } + return -ENOIOCTLCMD; +} + +static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + switch (icd->current_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + ceu_write(pcdev, CLFCR, !ctrl->value); + return 0; + } + return -EINVAL; + } + return -ENOIOCTLCMD; +} + +static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Low-pass filter", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, + .put_formats = sh_mobile_ceu_put_formats, .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, + .set_ctrl = sh_mobile_ceu_set_ctrl, + .get_ctrl = sh_mobile_ceu_get_ctrl, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf = sh_mobile_ceu_init_videobuf, + .controls = sh_mobile_ceu_controls, + .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; -static int sh_mobile_ceu_probe(struct platform_device *pdev) +static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) { struct sh_mobile_ceu_dev *pcdev; struct resource *res; void __iomem *base; - char clk_name[8]; unsigned int irq; int err = 0; @@ -865,28 +1711,22 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_release_mem; } - snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id); - pcdev->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(pcdev->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - err = PTR_ERR(pcdev->clk); - goto exit_free_irq; - } + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); pcdev->ici.priv = pcdev; - pcdev->ici.dev = &pdev->dev; + pcdev->ici.v4l2_dev.dev = &pdev->dev; pcdev->ici.nr = pdev->id; pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_clk; + goto exit_free_irq; return 0; -exit_free_clk: - clk_put(pcdev->clk); exit_free_irq: free_irq(pcdev->irq, pcdev); exit_release_mem: @@ -900,14 +1740,13 @@ exit: return err; } -static int sh_mobile_ceu_remove(struct platform_device *pdev) +static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, struct sh_mobile_ceu_dev, ici); soc_camera_host_unregister(soc_host); - clk_put(pcdev->clk); free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); @@ -916,12 +1755,30 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) return 0; } +static int sh_mobile_ceu_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { + .runtime_suspend = sh_mobile_ceu_runtime_nop, + .runtime_resume = sh_mobile_ceu_runtime_nop, +}; + static struct platform_driver sh_mobile_ceu_driver = { .driver = { .name = "sh_mobile_ceu", + .pm = &sh_mobile_ceu_dev_pm_ops, }, .probe = sh_mobile_ceu_probe, - .remove = sh_mobile_ceu_remove, + .remove = __exit_p(sh_mobile_ceu_remove), }; static int __init sh_mobile_ceu_init(void) @@ -940,3 +1797,4 @@ module_exit(sh_mobile_ceu_exit); MODULE_DESCRIPTION("SuperH Mobile CEU driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sh_mobile_ceu"); diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 23edfdc4d4b..9d84c94e8a4 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1954,8 +1954,10 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + msecs_to_jiffies( + cam->module_param.frame_timeout * 1000 + ) + ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 38a716020d7..36ee43a9ee9 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -123,8 +123,8 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, +#endif { } }; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9f5ae816785..59aa7a3694c 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -21,15 +21,15 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/module.h> #include <linux/mutex.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/vmalloc.h> #include <media/soc_camera.h> #include <media/v4l2-common.h> -#include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-dev.h> #include <media/videobuf-core.h> /* Default to VGA resolution */ @@ -38,7 +38,7 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); -static DEFINE_MUTEX(list_lock); +static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) @@ -152,12 +152,9 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - int ret = 0; - - if (icd->ops->set_std) - ret = icd->ops->set_std(icd, a); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return ret; + return v4l2_subdev_call(sd, core, s_std, *a); } static int soc_camera_reqbufs(struct file *file, void *priv, @@ -170,8 +167,6 @@ static int soc_camera_reqbufs(struct file *file, void *priv, WARN_ON(priv != file->private_data); - dev_dbg(&icd->dev, "%s: %d\n", __func__, p->memory); - ret = videobuf_reqbufs(&icf->vb_vidq, p); if (ret < 0) return ret; @@ -209,10 +204,11 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); } +/* Always entered with .video_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int i, fmts = 0; + int i, fmts = 0, ret; if (!ici->ops->get_formats) /* @@ -225,8 +221,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) * First pass - only count formats this host-sensor * configuration can provide */ - for (i = 0; i < icd->num_formats; i++) - fmts += ici->ops->get_formats(icd, i, NULL); + for (i = 0; i < icd->num_formats; i++) { + ret = ici->ops->get_formats(icd, i, NULL); + if (ret < 0) + return ret; + fmts += ret; + } if (!fmts) return -ENXIO; @@ -248,20 +248,39 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) icd->user_formats[i].cam_fmt = icd->formats + i; icd->user_formats[i].buswidth = icd->formats[i].depth; } else { - fmts += ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); + ret = ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + if (ret < 0) + goto egfmt; + fmts += ret; } icd->current_fmt = icd->user_formats[0].host_fmt; return 0; + +egfmt: + icd->num_user_formats = 0; + vfree(icd->user_formats); + return ret; } +/* Always entered with .video_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + + if (ici->ops->put_formats) + ici->ops->put_formats(icd); + icd->current_fmt = NULL; + icd->num_user_formats = 0; vfree(icd->user_formats); + icd->user_formats = NULL; } +#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ + ((x) >> 24) & 0xff + /* Called with .vb_lock held */ static int soc_camera_set_fmt(struct soc_camera_file *icf, struct v4l2_format *f) @@ -271,6 +290,9 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, struct v4l2_pix_format *pix = &f->fmt.pix; int ret; + dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + /* We always call try_fmt() before set_fmt() or set_crop() */ ret = ici->ops->try_fmt(icd, f); if (ret < 0) @@ -281,13 +303,13 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, return ret; } else if (!icd->current_fmt || icd->current_fmt->fourcc != pix->pixelformat) { - dev_err(ici->dev, + dev_err(&icd->dev, "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } - icd->width = pix->width; - icd->height = pix->height; + icd->user_width = pix->width; + icd->user_height = pix->height; icf->vb_vidq.field = icd->field = pix->field; @@ -296,7 +318,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, f->type); dev_dbg(&icd->dev, "set width: %d height: %d\n", - icd->width, icd->height); + icd->user_width, icd->user_height); /* set physical bus parameters */ return ici->ops->set_bus_param(icd, pix->pixelformat); @@ -304,30 +326,24 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, static int soc_camera_open(struct file *file) { - struct video_device *vdev; - struct soc_camera_device *icd; + struct video_device *vdev = video_devdata(file); + struct soc_camera_device *icd = container_of(vdev->parent, + struct soc_camera_device, + dev); + struct soc_camera_link *icl = to_soc_camera_link(icd); struct soc_camera_host *ici; struct soc_camera_file *icf; int ret; - icf = vmalloc(sizeof(*icf)); - if (!icf) - return -ENOMEM; - - /* - * It is safe to dereference these pointers now as long as a user has - * the video device open - we are protected by the held cdev reference. - */ + if (!icd->ops) + /* No device driver attached */ + return -ENODEV; - vdev = video_devdata(file); - icd = container_of(vdev->parent, struct soc_camera_device, dev); ici = to_soc_camera_host(icd->dev.parent); - if (!try_module_get(icd->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); - ret = -EINVAL; - goto emgd; - } + icf = vmalloc(sizeof(*icf)); + if (!icf) + return -ENOMEM; if (!try_module_get(ici->ops->owner)) { dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); @@ -335,7 +351,10 @@ static int soc_camera_open(struct file *file) goto emgi; } - /* Protect against icd->remove() until we module_get() both drivers. */ + /* + * Protect against icd->ops->remove() until we module_get() both + * drivers. + */ mutex_lock(&icd->video_lock); icf->icd = icd; @@ -347,14 +366,24 @@ static int soc_camera_open(struct file *file) struct v4l2_format f = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .fmt.pix = { - .width = icd->width, - .height = icd->height, + .width = icd->user_width, + .height = icd->user_height, .field = icd->field, .pixelformat = icd->current_fmt->fourcc, .colorspace = icd->current_fmt->colorspace, }, }; + if (icl->power) { + ret = icl->power(icd->pdev, 1); + if (ret < 0) + goto epower; + } + + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); @@ -367,28 +396,29 @@ static int soc_camera_open(struct file *file) goto esfmt; } - mutex_unlock(&icd->video_lock); - file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); ici->ops->init_videobuf(&icf->vb_vidq, icd); + mutex_unlock(&icd->video_lock); + return 0; /* - * First three errors are entered with the .video_lock held + * First five errors are entered with the .video_lock held * and use_count == 1 */ esfmt: ici->ops->remove(icd); eiciadd: + if (icl->power) + icl->power(icd->pdev, 0); +epower: icd->use_count--; mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); emgi: - module_put(icd->ops->owner); -emgd: vfree(icf); return ret; } @@ -398,21 +428,24 @@ static int soc_camera_close(struct file *file) struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct video_device *vdev = icd->vdev; mutex_lock(&icd->video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { + struct soc_camera_link *icl = to_soc_camera_link(icd); + ici->ops->remove(icd); + if (icl->power) + icl->power(icd->pdev, 0); + } mutex_unlock(&icd->video_lock); - module_put(icd->ops->owner); module_put(ici->ops->owner); vfree(icf); - dev_dbg(vdev->parent, "camera device close\n"); + dev_dbg(&icd->dev, "camera device close\n"); return 0; } @@ -422,10 +455,9 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct video_device *vdev = icd->vdev; int err = -EINVAL; - dev_err(vdev->parent, "camera device read not implemented\n"); + dev_err(&icd->dev, "camera device read not implemented\n"); return err; } @@ -483,8 +515,8 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, mutex_lock(&icf->vb_vidq.vb_lock); - if (videobuf_queue_is_busy(&icf->vb_vidq)) { - dev_err(&icd->dev, "S_FMT denied: queue busy\n"); + if (icf->vb_vidq.bufs[0]) { + dev_err(&icd->dev, "S_FMT denied: queue initialised\n"); ret = -EBUSY; goto unlock; } @@ -525,8 +557,8 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); - pix->width = icd->width; - pix->height = icd->height; + pix->width = icd->user_width; + pix->height = icd->user_height; pix->field = icf->vb_vidq.field; pix->pixelformat = icd->current_fmt->fourcc; pix->bytesperline = pix->width * @@ -555,18 +587,17 @@ static int soc_camera_streamon(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; WARN_ON(priv != file->private_data); - dev_dbg(&icd->dev, "%s\n", __func__); - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; mutex_lock(&icd->video_lock); - icd->ops->start_capture(icd); + v4l2_subdev_call(sd, video, s_stream, 1); /* This calls buf_queue from host driver's videobuf_queue_ops */ ret = videobuf_streamon(&icf->vb_vidq); @@ -581,11 +612,10 @@ static int soc_camera_streamoff(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); WARN_ON(priv != file->private_data); - dev_dbg(&icd->dev, "%s\n", __func__); - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -595,7 +625,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, * remaining buffers. When the last buffer is freed, stop capture */ videobuf_streamoff(&icf->vb_vidq); - icd->ops->stop_capture(icd); + v4l2_subdev_call(sd, video, s_stream, 0); mutex_unlock(&icd->video_lock); @@ -607,6 +637,7 @@ static int soc_camera_queryctrl(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); int i; WARN_ON(priv != file->private_data); @@ -614,6 +645,15 @@ static int soc_camera_queryctrl(struct file *file, void *priv, if (!qc->id) return -EINVAL; + /* First check host controls */ + for (i = 0; i < ici->ops->num_controls; i++) + if (qc->id == ici->ops->controls[i].id) { + memcpy(qc, &(ici->ops->controls[i]), + sizeof(*qc)); + return 0; + } + + /* Then device controls */ for (i = 0; i < icd->ops->num_controls; i++) if (qc->id == icd->ops->controls[i].id) { memcpy(qc, &(icd->ops->controls[i]), @@ -629,25 +669,19 @@ static int soc_camera_g_ctrl(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; WARN_ON(priv != file->private_data); - switch (ctrl->id) { - case V4L2_CID_GAIN: - if (icd->gain == (unsigned short)~0) - return -EINVAL; - ctrl->value = icd->gain; - return 0; - case V4L2_CID_EXPOSURE: - if (icd->exposure == (unsigned short)~0) - return -EINVAL; - ctrl->value = icd->exposure; - return 0; + if (ici->ops->get_ctrl) { + ret = ici->ops->get_ctrl(icd, ctrl); + if (ret != -ENOIOCTLCMD) + return ret; } - if (icd->ops->get_control) - return icd->ops->get_control(icd, ctrl); - return -EINVAL; + return v4l2_subdev_call(sd, core, g_ctrl, ctrl); } static int soc_camera_s_ctrl(struct file *file, void *priv, @@ -655,12 +689,19 @@ static int soc_camera_s_ctrl(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; WARN_ON(priv != file->private_data); - if (icd->ops->set_control) - return icd->ops->set_control(icd, ctrl); - return -EINVAL; + if (ici->ops->set_ctrl) { + ret = ici->ops->set_ctrl(icd, ctrl); + if (ret != -ENOIOCTLCMD) + return ret; + } + + return v4l2_subdev_call(sd, core, s_ctrl, ctrl); } static int soc_camera_cropcap(struct file *file, void *fh, @@ -668,20 +709,9 @@ static int soc_camera_cropcap(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->bounds.left = icd->x_min; - a->bounds.top = icd->y_min; - a->bounds.width = icd->width_max; - a->bounds.height = icd->height_max; - a->defrect.left = icd->x_min; - a->defrect.top = icd->y_min; - a->defrect.width = DEFAULT_WIDTH; - a->defrect.height = DEFAULT_HEIGHT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; + return ici->ops->cropcap(icd, a); } static int soc_camera_g_crop(struct file *file, void *fh, @@ -689,36 +719,53 @@ static int soc_camera_g_crop(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int ret; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->c.left = icd->x_current; - a->c.top = icd->y_current; - a->c.width = icd->width; - a->c.height = icd->height; + mutex_lock(&icf->vb_vidq.vb_lock); + ret = ici->ops->get_crop(icd, a); + mutex_unlock(&icf->vb_vidq.vb_lock); - return 0; + return ret; } +/* + * According to the V4L2 API, drivers shall not update the struct v4l2_crop + * argument with the actual geometry, instead, the user shall use G_CROP to + * retrieve it. However, we expect camera host and client drivers to update + * the argument, which we then use internally, but do not return to the user. + */ static int soc_camera_s_crop(struct file *file, void *fh, struct v4l2_crop *a) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_rect *rect = &a->c; + struct v4l2_crop current_crop; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n", + rect->width, rect->height, rect->left, rect->top); + /* Cropping is allowed during a running capture, guard consistency */ mutex_lock(&icf->vb_vidq.vb_lock); - ret = ici->ops->set_crop(icd, &a->c); - if (!ret) { - icd->width = a->c.width; - icd->height = a->c.height; - icd->x_current = a->c.left; - icd->y_current = a->c.top; + /* If get_crop fails, we'll let host and / or client drivers decide */ + ret = ici->ops->get_crop(icd, ¤t_crop); + + /* Prohibit window size change with initialised buffers */ + if (icf->vb_vidq.bufs[0] && !ret && + (a->c.width != current_crop.c.width || + a->c.height != current_crop.c.height)) { + dev_err(&icd->dev, + "S_CROP denied: queue initialised and sizes differ\n"); + ret = -EBUSY; + } else { + ret = ici->ops->set_crop(icd, a); } mutex_unlock(&icf->vb_vidq.vb_lock); @@ -731,11 +778,9 @@ static int soc_camera_g_chip_ident(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->get_chip_id) - return -EINVAL; - - return icd->ops->get_chip_id(icd, id); + return v4l2_subdev_call(sd, core, g_chip_ident, id); } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -744,11 +789,9 @@ static int soc_camera_g_register(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->get_register) - return -EINVAL; - - return icd->ops->get_register(icd, reg); + return v4l2_subdev_call(sd, core, g_register, reg); } static int soc_camera_s_register(struct file *file, void *fh, @@ -756,37 +799,12 @@ static int soc_camera_s_register(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->set_register) - return -EINVAL; - - return icd->ops->set_register(icd, reg); + return v4l2_subdev_call(sd, core, s_register, reg); } #endif -static int device_register_link(struct soc_camera_device *icd) -{ - int ret = dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); - - if (!ret) - ret = device_register(&icd->dev); - - if (ret < 0) { - /* Prevent calling device_unregister() */ - icd->dev.parent = NULL; - dev_err(&icd->dev, "Cannot register device: %d\n", ret); - /* Even if probe() was unsuccessful for all registered drivers, - * device_register() returns 0, and we add the link, just to - * document this camera's control device */ - } else if (icd->control) - /* Have to sysfs_remove_link() before device_unregister()? */ - if (sysfs_create_link(&icd->dev.kobj, &icd->control->kobj, - "control")) - dev_warn(&icd->dev, - "Failed creating the control symlink\n"); - return ret; -} - /* So far this function cannot fail */ static void scan_add_host(struct soc_camera_host *ici) { @@ -796,106 +814,193 @@ static void scan_add_host(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { - icd->dev.parent = ici->dev; - device_register_link(icd); + int ret; + icd->dev.parent = ici->v4l2_dev.dev; + dev_set_name(&icd->dev, "%u-%u", icd->iface, + icd->devnum); + ret = device_register(&icd->dev); + if (ret < 0) { + icd->dev.parent = NULL; + dev_err(&icd->dev, + "Cannot register device: %d\n", ret); + } } } mutex_unlock(&list_lock); } -/* return: 0 if no match found or a match found and - * device_register() successful, error code otherwise */ -static int scan_add_device(struct soc_camera_device *icd) +#ifdef CONFIG_I2C_BOARDINFO +static int soc_camera_init_i2c(struct soc_camera_device *icd, + struct soc_camera_link *icl) { - struct soc_camera_host *ici; - int ret = 0; + struct i2c_client *client; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); + struct v4l2_subdev *subdev; + int ret; - mutex_lock(&list_lock); + if (!adap) { + ret = -ENODEV; + dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n", + icl->i2c_adapter_id); + goto ei2cga; + } - list_add_tail(&icd->list, &devices); + icl->board_info->platform_data = icd; - /* Watch out for class_for_each_device / class_find_device API by - * Dave Young <hidave.darkstar@gmail.com> */ - list_for_each_entry(ici, &hosts, list) { - if (icd->iface == ici->nr) { - ret = 1; - icd->dev.parent = ici->dev; - break; - } + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, + icl->module_name, icl->board_info, NULL); + if (!subdev) { + ret = -ENOMEM; + goto ei2cnd; } - mutex_unlock(&list_lock); + client = subdev->priv; - if (ret) - ret = device_register_link(icd); + /* Use to_i2c_client(dev) to recover the i2c client */ + dev_set_drvdata(&icd->dev, &client->dev); + return 0; +ei2cnd: + i2c_put_adapter(adap); +ei2cga: return ret; } +static void soc_camera_free_i2c(struct soc_camera_device *icd) +{ + struct i2c_client *client = + to_i2c_client(to_soc_camera_control(icd)); + dev_set_drvdata(&icd->dev, NULL); + v4l2_device_unregister_subdev(i2c_get_clientdata(client)); + i2c_unregister_device(client); + i2c_put_adapter(client->adapter); +} +#else +#define soc_camera_init_i2c(icd, icl) (-ENODEV) +#define soc_camera_free_i2c(icd) do {} while (0) +#endif + +static int soc_camera_video_start(struct soc_camera_device *icd); +static int video_dev_create(struct soc_camera_device *icd); +/* Called during host-driver probe */ static int soc_camera_probe(struct device *dev) { struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(dev->parent); + struct soc_camera_link *icl = to_soc_camera_link(icd); + struct device *control = NULL; + struct v4l2_subdev *sd; + struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE}; int ret; - /* - * Possible race scenario: - * modprobe <camera-host-driver> triggers __func__ - * at this moment respective <camera-sensor-driver> gets rmmod'ed - * to protect take module references. - */ + dev_info(dev, "Probing %s\n", dev_name(dev)); - if (!try_module_get(icd->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); - ret = -EINVAL; - goto emgd; + if (icl->power) { + ret = icl->power(icd->pdev, 1); + if (ret < 0) { + dev_err(dev, + "Platform failed to power-on the camera.\n"); + goto epower; + } } - if (!try_module_get(ici->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + + /* Must have icd->vdev before registering the device */ + ret = video_dev_create(icd); + if (ret < 0) + goto evdc; + + /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ + if (icl->board_info) { + ret = soc_camera_init_i2c(icd, icl); + if (ret < 0) + goto eadddev; + } else if (!icl->add_device || !icl->del_device) { ret = -EINVAL; - goto emgi; + goto eadddev; + } else { + if (icl->module_name) + ret = request_module(icl->module_name); + + ret = icl->add_device(icl, &icd->dev); + if (ret < 0) + goto eadddev; + + /* + * FIXME: this is racy, have to use driver-binding notification, + * when it is available + */ + control = to_soc_camera_control(icd); + if (!control || !control->driver || !dev_get_drvdata(control) || + !try_module_get(control->driver->owner)) { + icl->del_device(icl); + goto enodrv; + } } + /* At this point client .probe() should have run already */ + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; + + icd->field = V4L2_FIELD_ANY; + + /* ..._video_start() will create a device node, so we have to protect */ mutex_lock(&icd->video_lock); - /* We only call ->add() here to activate and probe the camera. - * We shall ->remove() and deactivate it immediately afterwards. */ - ret = ici->ops->add(icd); + ret = soc_camera_video_start(icd); if (ret < 0) - goto eiadd; + goto evidstart; + + /* Try to improve our guess of a reasonable window format */ + sd = soc_camera_to_subdev(icd); + if (!v4l2_subdev_call(sd, video, g_fmt, &f)) { + icd->user_width = f.fmt.pix.width; + icd->user_height = f.fmt.pix.height; + } - ret = icd->ops->probe(icd); - if (ret >= 0) { - const struct v4l2_queryctrl *qctrl; + /* Do we have to sysfs_remove_link() before device_unregister()? */ + if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj, + "control")) + dev_warn(&icd->dev, "Failed creating the control symlink\n"); - qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); - icd->gain = qctrl ? qctrl->default_value : (unsigned short)~0; - qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = qctrl ? qctrl->default_value : - (unsigned short)~0; + ici->ops->remove(icd); - ret = soc_camera_init_user_formats(icd); - if (ret < 0) { - if (icd->ops->remove) - icd->ops->remove(icd); - goto eiufmt; - } + if (icl->power) + icl->power(icd->pdev, 0); - icd->height = DEFAULT_HEIGHT; - icd->width = DEFAULT_WIDTH; - icd->field = V4L2_FIELD_ANY; - } + mutex_unlock(&icd->video_lock); + return 0; + +evidstart: + mutex_unlock(&icd->video_lock); + soc_camera_free_user_formats(icd); eiufmt: + if (icl->board_info) { + soc_camera_free_i2c(icd); + } else { + icl->del_device(icl); + module_put(control->driver->owner); + } +enodrv: +eadddev: + video_device_release(icd->vdev); +evdc: ici->ops->remove(icd); -eiadd: - mutex_unlock(&icd->video_lock); - module_put(ici->ops->owner); -emgi: - module_put(icd->ops->owner); -emgd: +eadd: + if (icl->power) + icl->power(icd->pdev, 0); +epower: return ret; } @@ -904,12 +1009,28 @@ emgd: static int soc_camera_remove(struct device *dev) { struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct soc_camera_link *icl = to_soc_camera_link(icd); + struct video_device *vdev = icd->vdev; - mutex_lock(&icd->video_lock); - if (icd->ops->remove) - icd->ops->remove(icd); - mutex_unlock(&icd->video_lock); + BUG_ON(!dev->parent); + if (vdev) { + mutex_lock(&icd->video_lock); + video_unregister_device(vdev); + icd->vdev = NULL; + mutex_unlock(&icd->video_lock); + } + + if (icl->board_info) { + soc_camera_free_i2c(icd); + } else { + struct device_driver *drv = to_soc_camera_control(icd) ? + to_soc_camera_control(icd)->driver : NULL; + if (drv) { + icl->del_device(icl); + module_put(drv->owner); + } + } soc_camera_free_user_formats(icd); return 0; @@ -957,14 +1078,33 @@ static void dummy_release(struct device *dev) { } +static int default_cropcap(struct soc_camera_device *icd, + struct v4l2_cropcap *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, cropcap, a); +} + +static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, g_crop, a); +} + +static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, s_crop, a); +} + int soc_camera_host_register(struct soc_camera_host *ici) { struct soc_camera_host *ix; + int ret; if (!ici || !ici->ops || !ici->ops->try_fmt || !ici->ops->set_fmt || - !ici->ops->set_crop || !ici->ops->set_bus_param || !ici->ops->querycap || !ici->ops->init_videobuf || @@ -972,18 +1112,27 @@ int soc_camera_host_register(struct soc_camera_host *ici) !ici->ops->add || !ici->ops->remove || !ici->ops->poll || - !ici->dev) + !ici->v4l2_dev.dev) return -EINVAL; + if (!ici->ops->set_crop) + ici->ops->set_crop = default_s_crop; + if (!ici->ops->get_crop) + ici->ops->get_crop = default_g_crop; + if (!ici->ops->cropcap) + ici->ops->cropcap = default_cropcap; + mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { if (ix->nr == ici->nr) { - mutex_unlock(&list_lock); - return -EBUSY; + ret = -EBUSY; + goto edevreg; } } - dev_set_drvdata(ici->dev, ici); + ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); + if (ret < 0) + goto edevreg; list_add_tail(&ici->list, &hosts); mutex_unlock(&list_lock); @@ -991,6 +1140,10 @@ int soc_camera_host_register(struct soc_camera_host *ici) scan_add_host(ici); return 0; + +edevreg: + mutex_unlock(&list_lock); + return ret; } EXPORT_SYMBOL(soc_camera_host_register); @@ -1004,42 +1157,34 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) list_del(&ici->list); list_for_each_entry(icd, &devices, list) { - if (icd->dev.parent == ici->dev) { + if (icd->iface == ici->nr) { + /* The bus->remove will be called */ device_unregister(&icd->dev); /* Not before device_unregister(), .remove * needs parent to call ici->ops->remove() */ icd->dev.parent = NULL; + + /* If the host module is loaded again, device_register() + * would complain "already initialised" */ memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj)); } } mutex_unlock(&list_lock); - dev_set_drvdata(ici->dev, NULL); + v4l2_device_unregister(&ici->v4l2_dev); } EXPORT_SYMBOL(soc_camera_host_unregister); /* Image capture device */ -int soc_camera_device_register(struct soc_camera_device *icd) +static int soc_camera_device_register(struct soc_camera_device *icd) { struct soc_camera_device *ix; int num = -1, i; - if (!icd || !icd->ops || - !icd->ops->probe || - !icd->ops->init || - !icd->ops->release || - !icd->ops->start_capture || - !icd->ops->stop_capture || - !icd->ops->set_crop || - !icd->ops->set_fmt || - !icd->ops->try_fmt || - !icd->ops->query_bus_param || - !icd->ops->set_bus_param) - return -EINVAL; - for (i = 0; i < 256 && num < 0; i++) { num = i; + /* Check if this index is available on this interface */ list_for_each_entry(ix, &devices, list) { if (ix->iface == icd->iface && ix->devnum == i) { num = -1; @@ -1061,21 +1206,15 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->host_priv = NULL; mutex_init(&icd->video_lock); - return scan_add_device(icd); + list_add_tail(&icd->list, &devices); + + return 0; } -EXPORT_SYMBOL(soc_camera_device_register); -void soc_camera_device_unregister(struct soc_camera_device *icd) +static void soc_camera_device_unregister(struct soc_camera_device *icd) { - mutex_lock(&list_lock); list_del(&icd->list); - - /* The bus->remove will be eventually called */ - if (icd->dev.parent) - device_unregister(&icd->dev); - mutex_unlock(&list_lock); } -EXPORT_SYMBOL(soc_camera_device_unregister); static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_querycap = soc_camera_querycap, @@ -1106,23 +1245,13 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { #endif }; -/* - * Usually called from the struct soc_camera_ops .probe() method, i.e., from - * soc_camera_probe() above with .video_lock held - */ -int soc_camera_video_start(struct soc_camera_device *icd) +static int video_dev_create(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int err = -ENOMEM; - struct video_device *vdev; + struct video_device *vdev = video_device_alloc(); - if (!icd->dev.parent) - return -ENODEV; - - vdev = video_device_alloc(); if (!vdev) - goto evidallocd; - dev_dbg(ici->dev, "Allocated video_device %p\n", vdev); + return -ENOMEM; strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); @@ -1132,87 +1261,93 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; vdev->minor = -1; - vdev->tvnorms = V4L2_STD_UNKNOWN, + vdev->tvnorms = V4L2_STD_UNKNOWN; - err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); - if (err < 0) { - dev_err(vdev->parent, "video_register_device failed\n"); - goto evidregd; - } icd->vdev = vdev; return 0; - -evidregd: - video_device_release(vdev); -evidallocd: - return err; } -EXPORT_SYMBOL(soc_camera_video_start); -/* Called from client .remove() methods with .video_lock held */ -void soc_camera_video_stop(struct soc_camera_device *icd) +/* + * Called from soc_camera_probe() above (with .video_lock held???) + */ +static int soc_camera_video_start(struct soc_camera_device *icd) { - struct video_device *vdev = icd->vdev; + int ret; - dev_dbg(&icd->dev, "%s\n", __func__); + if (!icd->dev.parent) + return -ENODEV; - if (!icd->dev.parent || !vdev) - return; + if (!icd->ops || + !icd->ops->query_bus_param || + !icd->ops->set_bus_param) + return -EINVAL; + + ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, + icd->vdev->minor); + if (ret < 0) { + dev_err(&icd->dev, "video_register_device failed: %d\n", ret); + return ret; + } - video_unregister_device(vdev); - icd->vdev = NULL; + return 0; } -EXPORT_SYMBOL(soc_camera_video_stop); static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { struct soc_camera_link *icl = pdev->dev.platform_data; - struct i2c_adapter *adap; - struct i2c_client *client; + struct soc_camera_device *icd; + int ret; if (!icl) return -EINVAL; - adap = i2c_get_adapter(icl->i2c_adapter_id); - if (!adap) { - dev_warn(&pdev->dev, "Cannot get adapter #%d. No driver?\n", - icl->i2c_adapter_id); - /* -ENODEV and -ENXIO do not produce an error on probe()... */ - return -ENOENT; - } - - icl->board_info->platform_data = icl; - client = i2c_new_device(adap, icl->board_info); - if (!client) { - i2c_put_adapter(adap); + icd = kzalloc(sizeof(*icd), GFP_KERNEL); + if (!icd) return -ENOMEM; - } - platform_set_drvdata(pdev, client); + icd->iface = icl->bus_id; + icd->pdev = &pdev->dev; + platform_set_drvdata(pdev, icd); + icd->dev.platform_data = icl; + + ret = soc_camera_device_register(icd); + if (ret < 0) + goto escdevreg; + + icd->user_width = DEFAULT_WIDTH; + icd->user_height = DEFAULT_HEIGHT; return 0; + +escdevreg: + kfree(icd); + + return ret; } +/* Only called on rmmod for each platform device, since they are not + * hot-pluggable. Now we know, that all our users - hosts and devices have + * been unloaded already */ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) { - struct i2c_client *client = platform_get_drvdata(pdev); + struct soc_camera_device *icd = platform_get_drvdata(pdev); - if (!client) - return -ENODEV; + if (!icd) + return -EINVAL; - i2c_unregister_device(client); - i2c_put_adapter(client->adapter); + soc_camera_device_unregister(icd); + + kfree(icd); return 0; } static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = __devexit_p(soc_camera_pdrv_remove), - .driver = { - .name = "soc-camera-pdrv", - .owner = THIS_MODULE, + .remove = __devexit_p(soc_camera_pdrv_remove), + .driver = { + .name = "soc-camera-pdrv", + .owner = THIS_MODULE, }, }; @@ -1225,7 +1360,7 @@ static int __init soc_camera_init(void) if (ret) goto edrvr; - ret = platform_driver_register(&soc_camera_pdrv); + ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe); if (ret) goto epdr; diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index c48676356ab..b6a575ce5da 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -16,54 +16,32 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/soc_camera_platform.h> struct soc_camera_platform_priv { - struct soc_camera_platform_info *info; - struct soc_camera_device icd; + struct v4l2_subdev subdev; struct soc_camera_data_format format; }; -static struct soc_camera_platform_info * -soc_camera_platform_get_info(struct soc_camera_device *icd) +static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) { - struct soc_camera_platform_priv *priv; - priv = container_of(icd, struct soc_camera_platform_priv, icd); - return priv->info; -} - -static int soc_camera_platform_init(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - - if (p->power) - p->power(1); - - return 0; -} - -static int soc_camera_platform_release(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - - if (p->power) - p->power(0); - - return 0; + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + return container_of(subdev, struct soc_camera_platform_priv, subdev); } -static int soc_camera_platform_start_capture(struct soc_camera_device *icd) +static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - return p->set_capture(p, 1); + struct platform_device *pdev = + to_platform_device(to_soc_camera_control(icd)); + return pdev->dev.platform_data; } -static int soc_camera_platform_stop_capture(struct soc_camera_device *icd) +static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - return p->set_capture(p, 0); + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + return p->set_capture(p, enable); } static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, @@ -75,26 +53,14 @@ static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, static unsigned long soc_camera_platform_query_bus_param(struct soc_camera_device *icd) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + struct soc_camera_platform_info *p = get_info(icd); return p->bus_param; } -static int soc_camera_platform_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) -{ - return 0; -} - -static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, +static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - return 0; -} - -static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); struct v4l2_pix_format *pix = &f->fmt.pix; pix->width = p->format.width; @@ -102,82 +68,99 @@ static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, return 0; } -static int soc_camera_platform_video_probe(struct soc_camera_device *icd) +static void soc_camera_platform_video_probe(struct soc_camera_device *icd, + struct platform_device *pdev) { - struct soc_camera_platform_priv *priv; - priv = container_of(icd, struct soc_camera_platform_priv, icd); + struct soc_camera_platform_priv *priv = get_priv(pdev); + struct soc_camera_platform_info *p = pdev->dev.platform_data; - priv->format.name = priv->info->format_name; - priv->format.depth = priv->info->format_depth; - priv->format.fourcc = priv->info->format.pixelformat; - priv->format.colorspace = priv->info->format.colorspace; + priv->format.name = p->format_name; + priv->format.depth = p->format_depth; + priv->format.fourcc = p->format.pixelformat; + priv->format.colorspace = p->format.colorspace; icd->formats = &priv->format; icd->num_formats = 1; - - return soc_camera_video_start(icd); } -static void soc_camera_platform_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); -} +static struct v4l2_subdev_core_ops platform_subdev_core_ops; + +static struct v4l2_subdev_video_ops platform_subdev_video_ops = { + .s_stream = soc_camera_platform_s_stream, + .try_fmt = soc_camera_platform_try_fmt, +}; + +static struct v4l2_subdev_ops platform_subdev_ops = { + .core = &platform_subdev_core_ops, + .video = &platform_subdev_video_ops, +}; static struct soc_camera_ops soc_camera_platform_ops = { - .owner = THIS_MODULE, - .probe = soc_camera_platform_video_probe, - .remove = soc_camera_platform_video_remove, - .init = soc_camera_platform_init, - .release = soc_camera_platform_release, - .start_capture = soc_camera_platform_start_capture, - .stop_capture = soc_camera_platform_stop_capture, - .set_crop = soc_camera_platform_set_crop, - .set_fmt = soc_camera_platform_set_fmt, - .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, .query_bus_param = soc_camera_platform_query_bus_param, }; static int soc_camera_platform_probe(struct platform_device *pdev) { + struct soc_camera_host *ici; struct soc_camera_platform_priv *priv; - struct soc_camera_platform_info *p; + struct soc_camera_platform_info *p = pdev->dev.platform_data; struct soc_camera_device *icd; int ret; - p = pdev->dev.platform_data; if (!p) return -EINVAL; + if (!p->dev) { + dev_err(&pdev->dev, + "Platform has not set soc_camera_device pointer!\n"); + return -EINVAL; + } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->info = p; - platform_set_drvdata(pdev, priv); + icd = to_soc_camera_dev(p->dev); + + /* soc-camera convention: control's drvdata points to the subdev */ + platform_set_drvdata(pdev, &priv->subdev); + /* Set the control device reference */ + dev_set_drvdata(&icd->dev, &pdev->dev); + + icd->y_skip_top = 0; + icd->ops = &soc_camera_platform_ops; + + ici = to_soc_camera_host(icd->dev.parent); - icd = &priv->icd; - icd->ops = &soc_camera_platform_ops; - icd->control = &pdev->dev; - icd->width_min = 0; - icd->width_max = priv->info->format.width; - icd->height_min = 0; - icd->height_max = priv->info->format.height; - icd->y_skip_top = 0; - icd->iface = priv->info->iface; + soc_camera_platform_video_probe(icd, pdev); - ret = soc_camera_device_register(icd); + v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, p); + strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE); + + ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); if (ret) - kfree(priv); + goto evdrs; + + return ret; +evdrs: + icd->ops = NULL; + platform_set_drvdata(pdev, NULL); + kfree(priv); return ret; } static int soc_camera_platform_remove(struct platform_device *pdev) { - struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev); + struct soc_camera_platform_priv *priv = get_priv(pdev); + struct soc_camera_platform_info *p = pdev->dev.platform_data; + struct soc_camera_device *icd = to_soc_camera_dev(p->dev); - soc_camera_device_unregister(&priv->icd); + v4l2_device_unregister_subdev(&priv->subdev); + icd->ops = NULL; + platform_set_drvdata(pdev, NULL); kfree(priv); return 0; } @@ -185,6 +168,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev) static struct platform_driver soc_camera_platform_driver = { .driver = { .name = "soc_camera_platform", + .owner = THIS_MODULE, }, .probe = soc_camera_platform_probe, .remove = soc_camera_platform_remove, @@ -206,3 +190,4 @@ module_exit(soc_camera_platform_module_exit); MODULE_DESCRIPTION("SoC Camera Platform driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:soc_camera_platform"); diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 4d6785e6345..0b996ea4134 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1050,8 +1050,8 @@ static int stk_setup_format(struct stk_camera *dev) depth = 1; else depth = 2; - while (stk_sizes[i].m != dev->vsettings.mode - && i < ARRAY_SIZE(stk_sizes)) + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) i++; if (i == ARRAY_SIZE(stk_sizes)) { STK_ERROR("Something is broken in %s\n", __func__); @@ -1400,7 +1400,6 @@ static int stk_camera_probe(struct usb_interface *interface, } stk_create_sysfs_files(&dev->vdev); - usb_autopm_enable(dev->interface); return 0; diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 8b4e7dafce7..6a91714125d 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -734,10 +734,6 @@ static int stv680_start_stream (struct usb_stv *stv680) return 0; nomem_err: - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } for (i = 0; i < STV680_NUMSBUF; i++) { usb_kill_urb(stv680->urb[i]); usb_free_urb(stv680->urb[i]); @@ -745,6 +741,11 @@ static int stv680_start_stream (struct usb_stv *stv680) kfree(stv680->sbuf[i].data); stv680->sbuf[i].data = NULL; } + /* used in irq, free only as all URBs are dead */ + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } return -ENOMEM; } diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 537594211a9..aba92e2313d 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -29,6 +29,7 @@ #include "tuner-simple.h" #include "tda9887.h" #include "xc5000.h" +#include "tda18271.h" #define UNSET (-1U) @@ -420,6 +421,17 @@ static void set_type(struct i2c_client *c, unsigned int type, goto attach_failed; break; } + case TUNER_NXP_TDA18271: + { + struct tda18271_config cfg = { + .config = t->config, + }; + + if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, + t->i2c->adapter, &cfg)) + goto attach_failed; + break; + } default: if (!dvb_attach(simple_tuner_attach, &t->fe, t->i2c->adapter, t->i2c->addr, t->type)) @@ -819,8 +831,8 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) fe_tuner_ops->get_frequency(&t->fe, &abs_freq); f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - (abs_freq * 2 + 125/2) / 125 : - (abs_freq + 62500/2) / 62500; + DIV_ROUND_CLOSEST(abs_freq * 2, 125) : + DIV_ROUND_CLOSEST(abs_freq, 62500); return 0; } f->frequency = (V4L2_TUNER_RADIO == t->mode) ? diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index ac02808106c..d533ea57e7b 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -646,14 +646,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tvee->has_radio = 1; } - if (tuner1 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { + if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { tvee->tuner_type = hauppauge_tuner[tuner1].id; t_name1 = hauppauge_tuner[tuner1].name; } else { t_name1 = "unknown"; } - if (tuner2 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { + if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { tvee->tuner2_type = hauppauge_tuner[tuner2].id; t_name2 = hauppauge_tuner[tuner2].name; } else { diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 3750f7fadb1..244372627df 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -31,7 +31,10 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> -#include <media/v4l2-int-device.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/tvp514x.h> #include "tvp514x_regs.h" @@ -49,15 +52,11 @@ static int debug; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#define dump_reg(client, reg, val) \ - do { \ - val = tvp514x_read_reg(client, reg); \ - v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ - } while (0) +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); -/** - * enum tvp514x_std - enum for supported standards - */ +/* enum tvp514x_std - enum for supported standards */ enum tvp514x_std { STD_NTSC_MJ = 0, STD_PAL_BDGHIN, @@ -65,14 +64,6 @@ enum tvp514x_std { }; /** - * enum tvp514x_state - enum for different decoder states - */ -enum tvp514x_state { - STATE_NOT_DETECTED, - STATE_DETECTED -}; - -/** * struct tvp514x_std_info - Structure to store standard informations * @width: Line width in pixels * @height:Number of active lines @@ -89,33 +80,27 @@ struct tvp514x_std_info { static struct tvp514x_reg tvp514x_reg_list_default[0x40]; /** * struct tvp514x_decoder - TVP5146/47 decoder object - * @v4l2_int_device: Slave handle - * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @sd: Subdevice Slave handle * @tvp514x_regs: copy of hw's regs with preset values. * @pdata: Board specific - * @client: I2C client data - * @id: Entry from I2C table * @ver: Chip version - * @state: TVP5146/47 decoder state - detected or not-detected + * @streaming: TVP5146/47 decoder streaming - enabled or disabled. * @pix: Current pixel format * @num_fmts: Number of formats * @fmt_list: Format list * @current_std: Current standard * @num_stds: Number of standards * @std_list: Standards list - * @route: input and output routing at chip level + * @input: Input routing at chip level + * @output: Output routing at chip level */ struct tvp514x_decoder { - struct v4l2_int_device v4l2_int_device; - struct v4l2_int_slave tvp514x_slave; + struct v4l2_subdev sd; struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; - struct i2c_client *client; - - struct i2c_device_id *id; int ver; - enum tvp514x_state state; + int streaming; struct v4l2_pix_format pix; int num_fmts; @@ -124,15 +109,18 @@ struct tvp514x_decoder { enum tvp514x_std current_std; int num_stds; struct tvp514x_std_info *std_list; - - struct v4l2_routing route; + /* Input and Output Routing parameters */ + u32 input; + u32 output; }; /* TVP514x default register values */ static struct tvp514x_reg tvp514x_reg_list_default[] = { - {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + /* Composite selected */ + {TOK_WRITE, REG_INPUT_SEL, 0x05}, {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, - {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + /* Auto mode */ + {TOK_WRITE, REG_VIDEO_STD, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, {TOK_WRITE, REG_COLOR_KILLER, 0x10}, @@ -145,53 +133,74 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_WRITE, REG_HUE, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, - {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x0F, 0x00}, {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, - {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x13, 0x00}, {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, - {TOK_SKIP, 0x15, 0x00}, /* Reserved */ - {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + /* Reserved */ + {TOK_SKIP, 0x15, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, - {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, 0x26, 0x00}, /* Reserved */ - {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x26, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, - {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x29, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, - {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x2B, 0x00}, {TOK_SKIP, REG_SCART_DELAY, 0x00}, {TOK_SKIP, REG_CTI_DELAY, 0x00}, {TOK_SKIP, REG_CTI_CONTROL, 0x00}, - {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ - {TOK_SKIP, 0x30, 0x00}, /* Reserved */ - {TOK_SKIP, 0x31, 0x00}, /* Reserved */ - {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ - {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ - {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ - {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ - {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + /* Reserved */ + {TOK_SKIP, 0x2F, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, + /* HS, VS active high */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, + /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, + /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, + /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, + /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, - {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + /* Clear status */ + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, {TOK_TERM, 0, 0}, }; -/* List of image formats supported by TVP5146/47 decoder +/** + * List of image formats supported by TVP5146/47 decoder * Currently we are using 8 bit mode only, but can be * extended to 10/20 bit mode. */ @@ -205,7 +214,7 @@ static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { }, }; -/* +/** * Supported standards - * * Currently supports two standards only, need to add support for rest of the @@ -240,35 +249,32 @@ static struct tvp514x_std_info tvp514x_std_list[] = { }, /* Standard: need to add for additional standard */ }; -/* - * Control structure for Auto Gain - * This is temporary data, will get replaced once - * v4l2_ctrl_query_fill supports it. - */ -static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { - .id = V4L2_CID_AUTOGAIN, - .name = "Gain, Automatic", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, -}; -/* - * Read a value from a register in an TVP5146/47 decoder device. + +static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp514x_decoder, sd); +} + + +/** + * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * * Returns value read if successful, or non-zero (-1) otherwise. */ -static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + read_again: err = i2c_smbus_read_byte_data(client, reg); if (err == -1) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Read: retry ... %d\n", retry); + v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto read_again; @@ -278,20 +284,39 @@ read_again: return err; } -/* +/** + * dump_reg() - dump the register content of TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + */ +static void dump_reg(struct v4l2_subdev *sd, u8 reg) +{ + u32 val; + + val = tvp514x_read_reg(sd, reg); + v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); +} + +/** + * tvp514x_write_reg() - Write a value to a register in TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * @val: value to be written to the register + * * Write a value to a register in an TVP5146/47 decoder device. * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + write_again: err = i2c_smbus_write_byte_data(client, reg, val); if (err) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Write: retry ... %d\n", retry); + v4l2_warn(sd, "Write: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto write_again; @@ -301,17 +326,19 @@ write_again: return err; } -/* - * tvp514x_write_regs : Initializes a list of TVP5146/47 registers +/** + * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @reglist: list of TVP5146/47 registers and values + * + * Initializes a list of TVP5146/47 registers:- * if token is TOK_TERM, then entire write operation terminates * if token is TOK_DELAY, then a delay of 'val' msec is introduced * if token is TOK_SKIP, then the register write is skipped * if token is TOK_WRITE, then the register write is performed - * - * reglist - list of registers to be written * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_regs(struct i2c_client *client, +static int tvp514x_write_regs(struct v4l2_subdev *sd, const struct tvp514x_reg reglist[]) { int err; @@ -326,31 +353,33 @@ static int tvp514x_write_regs(struct i2c_client *client, if (next->token == TOK_SKIP) continue; - err = tvp514x_write_reg(client, next->reg, (u8) next->val); + err = tvp514x_write_reg(sd, next->reg, (u8) next->val); if (err) { - v4l_err(client, "Write failed. Err[%d]\n", err); + v4l2_err(sd, "Write failed. Err[%d]\n", err); return err; } } return 0; } -/* - * tvp514x_get_current_std: - * Returns the current standard detected by TVP5146/47 +/** + * tvp514x_get_current_std() : Get the current standard detected by TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * + * Get current standard detected by TVP5146/47, STD_INVALID if there is no + * standard detected. */ -static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder - *decoder) +static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) { u8 std, std_status; - std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); - if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + std = tvp514x_read_reg(sd, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) /* use the standard status register */ - std_status = tvp514x_read_reg(decoder->client, - REG_VIDEO_STD_STATUS); - } else - std_status = std; /* use the standard register itself */ + std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); + else + /* use the standard register itself */ + std_status = std; switch (std_status & VIDEO_STD_MASK) { case VIDEO_STD_NTSC_MJ_BIT: @@ -366,94 +395,99 @@ static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder return STD_INVALID; } -/* - * TVP5146/47 register dump function - */ -static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +/* TVP5146/47 register dump function */ +static void tvp514x_reg_dump(struct v4l2_subdev *sd) { - u8 value; - - dump_reg(decoder->client, REG_INPUT_SEL, value); - dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); - dump_reg(decoder->client, REG_VIDEO_STD, value); - dump_reg(decoder->client, REG_OPERATION_MODE, value); - dump_reg(decoder->client, REG_COLOR_KILLER, value); - dump_reg(decoder->client, REG_LUMA_CONTROL1, value); - dump_reg(decoder->client, REG_LUMA_CONTROL2, value); - dump_reg(decoder->client, REG_LUMA_CONTROL3, value); - dump_reg(decoder->client, REG_BRIGHTNESS, value); - dump_reg(decoder->client, REG_CONTRAST, value); - dump_reg(decoder->client, REG_SATURATION, value); - dump_reg(decoder->client, REG_HUE, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); - dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); - dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_SYNC_CONTROL, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); - dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); + dump_reg(sd, REG_INPUT_SEL); + dump_reg(sd, REG_AFE_GAIN_CTRL); + dump_reg(sd, REG_VIDEO_STD); + dump_reg(sd, REG_OPERATION_MODE); + dump_reg(sd, REG_COLOR_KILLER); + dump_reg(sd, REG_LUMA_CONTROL1); + dump_reg(sd, REG_LUMA_CONTROL2); + dump_reg(sd, REG_LUMA_CONTROL3); + dump_reg(sd, REG_BRIGHTNESS); + dump_reg(sd, REG_CONTRAST); + dump_reg(sd, REG_SATURATION); + dump_reg(sd, REG_HUE); + dump_reg(sd, REG_CHROMA_CONTROL1); + dump_reg(sd, REG_CHROMA_CONTROL2); + dump_reg(sd, REG_COMP_PR_SATURATION); + dump_reg(sd, REG_COMP_Y_CONTRAST); + dump_reg(sd, REG_COMP_PB_SATURATION); + dump_reg(sd, REG_COMP_Y_BRIGHTNESS); + dump_reg(sd, REG_AVID_START_PIXEL_LSB); + dump_reg(sd, REG_AVID_START_PIXEL_MSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); + dump_reg(sd, REG_VSYNC_START_LINE_LSB); + dump_reg(sd, REG_VSYNC_START_LINE_MSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); + dump_reg(sd, REG_VBLK_START_LINE_LSB); + dump_reg(sd, REG_VBLK_START_LINE_MSB); + dump_reg(sd, REG_VBLK_STOP_LINE_LSB); + dump_reg(sd, REG_VBLK_STOP_LINE_MSB); + dump_reg(sd, REG_SYNC_CONTROL); + dump_reg(sd, REG_OUTPUT_FORMATTER1); + dump_reg(sd, REG_OUTPUT_FORMATTER2); + dump_reg(sd, REG_OUTPUT_FORMATTER3); + dump_reg(sd, REG_OUTPUT_FORMATTER4); + dump_reg(sd, REG_OUTPUT_FORMATTER5); + dump_reg(sd, REG_OUTPUT_FORMATTER6); + dump_reg(sd, REG_CLEAR_LOST_LOCK); } -/* - * Configure the TVP5146/47 with the current register settings +/** + * tvp514x_configure() - Configure the TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @decoder: ptr to tvp514x_decoder structure + * * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_configure(struct tvp514x_decoder *decoder) +static int tvp514x_configure(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { int err; /* common register initialization */ err = - tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); + tvp514x_write_regs(sd, decoder->tvp514x_regs); if (err) return err; if (debug) - tvp514x_reg_dump(decoder); + tvp514x_reg_dump(sd); return 0; } -/* - * Detect if an tvp514x is present, and if so which revision. +/** + * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. + * @sd: pointer to standard V4L2 sub-device structure + * @decoder: pointer to tvp514x_decoder structure + * * A device is considered to be detected if the chip ID (LSB and MSB) * registers match the expected values. * Any value of the rom version register is accepted. * Returns ENODEV error number if no device is detected, or zero * if a device is detected. */ -static int tvp514x_detect(struct tvp514x_decoder *decoder) +static int tvp514x_detect(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { u8 chip_id_msb, chip_id_lsb, rom_ver; + struct i2c_client *client = v4l2_get_subdevdata(sd); - chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); - chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); - rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); - v4l_dbg(1, debug, decoder->client, + v4l2_dbg(1, debug, sd, "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", chip_id_msb, chip_id_lsb, rom_ver); if ((chip_id_msb != TVP514X_CHIP_ID_MSB) @@ -462,38 +496,30 @@ static int tvp514x_detect(struct tvp514x_decoder *decoder) /* We didn't read the values we expected, so this must not be * an TVP5146/47. */ - v4l_err(decoder->client, - "chip id mismatch msb:0x%x lsb:0x%x\n", - chip_id_msb, chip_id_lsb); + v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); return -ENODEV; } decoder->ver = rom_ver; - decoder->state = STATE_DETECTED; - v4l_info(decoder->client, - "%s found at 0x%x (%s)\n", decoder->client->name, - decoder->client->addr << 1, - decoder->client->adapter->name); + v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", + client->name, decoder->ver, + client->addr << 1, client->adapter->name); return 0; } -/* - * Following are decoder interface functions implemented by - * TVP5146/47 decoder driver. - */ - /** - * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_querystd() - V4L2 decoder interface handler for querystd + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 std_id ioctl enum * * Returns the current standard detected by TVP5146/47. If no active input is * detected, returns -EINVAL */ -static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); enum tvp514x_std current_std; enum tvp514x_input input_sel; u8 sync_lock_status, lock_mask; @@ -502,11 +528,11 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; - input_sel = decoder->route.input; + input_sel = decoder->input; switch (input_sel) { case INPUT_CVBS_VI1A: @@ -544,42 +570,39 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; } /* check whether signal is locked */ - sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask != (sync_lock_status & lock_mask)) return -EINVAL; /* No input detected */ decoder->current_std = current_std; *std_id = decoder->std_list[current_std].standard.id; - v4l_dbg(1, debug, decoder->client, "Current STD: %s", + v4l2_dbg(1, debug, sd, "Current STD: %s", decoder->std_list[current_std].standard.name); return 0; } /** - * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_std() - V4L2 decoder interface handler for s_std + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 v4l2_std_id ioctl enum * * If std_id is supported, sets the requested standard. Otherwise, returns * -EINVAL */ -static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err, i; - if (std_id == NULL) - return -EINVAL; - for (i = 0; i < decoder->num_stds; i++) - if (*std_id & decoder->std_list[i].standard.id) + if (std_id & decoder->std_list[i].standard.id) break; if ((i == decoder->num_stds) || (i == STD_INVALID)) return -EINVAL; - err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + err = tvp514x_write_reg(sd, REG_VIDEO_STD, decoder->std_list[i].video_std); if (err) return err; @@ -588,24 +611,26 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) decoder->tvp514x_regs[REG_VIDEO_STD].val = decoder->std_list[i].video_std; - v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + v4l2_dbg(1, debug, sd, "Standard set to: %s", decoder->std_list[i].standard.name); return 0; } /** - * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl - * @s: pointer to standard V4L2 device structure - * @index: number of the input + * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing + * @sd: pointer to standard V4L2 sub-device structure + * @input: input selector for routing the signal + * @output: output selector for routing the signal + * @config: config value. Not used * * If index is valid, selects the requested input. Otherwise, returns -EINVAL if * the input is not supported or there is no active signal present in the * selected input. */ -static int ioctl_s_routing(struct v4l2_int_device *s, - struct v4l2_routing *route) +static int tvp514x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err; enum tvp514x_input input_sel; enum tvp514x_output output_sel; @@ -613,20 +638,21 @@ static int ioctl_s_routing(struct v4l2_int_device *s, u8 sync_lock_status, lock_mask; int try_count = LOCK_RETRY_COUNT; - if ((!route) || (route->input >= INPUT_INVALID) || - (route->output >= OUTPUT_INVALID)) - return -EINVAL; /* Index out of bound */ + if ((input >= INPUT_INVALID) || + (output >= OUTPUT_INVALID)) + /* Index out of bound */ + return -EINVAL; - input_sel = route->input; - output_sel = route->output; + input_sel = input; + output_sel = output; - err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); if (err) return err; - output_sel |= tvp514x_read_reg(decoder->client, + output_sel |= tvp514x_read_reg(sd, REG_OUTPUT_FORMATTER1) & 0x7; - err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, output_sel); if (err) return err; @@ -637,7 +663,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, /* Clear status */ msleep(LOCK_RETRY_DELAY); err = - tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); if (err) return err; @@ -672,7 +698,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | STATUS_VIRT_SYNC_LOCK_BIT; break; - /*Need to add other interfaces*/ + /* Need to add other interfaces*/ default: return -EINVAL; } @@ -682,42 +708,41 @@ static int ioctl_s_routing(struct v4l2_int_device *s, msleep(LOCK_RETRY_DELAY); /* get the current standard for future reference */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) continue; - sync_lock_status = tvp514x_read_reg(decoder->client, + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask == (sync_lock_status & lock_mask)) - break; /* Input detected */ + /* Input detected */ + break; } if ((current_std == STD_INVALID) || (try_count < 0)) return -EINVAL; decoder->current_std = current_std; - decoder->route.input = route->input; - decoder->route.output = route->output; + decoder->input = input; + decoder->output = output; - v4l_dbg(1, debug, decoder->client, - "Input set to: %d, std : %d", + v4l2_dbg(1, debug, sd, "Input set to: %d, std : %d", input_sel, current_std); return 0; } /** - * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_queryctrl() - V4L2 decoder interface handler for queryctrl + * @sd: pointer to standard V4L2 sub-device structure * @qctrl: standard V4L2 v4l2_queryctrl structure * * If the requested control is supported, returns the control information. * Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) { - struct tvp514x_decoder *decoder = s->priv; int err = -EINVAL; if (qctrl == NULL) @@ -725,13 +750,13 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { case V4L2_CID_BRIGHTNESS: - /* Brightness supported is (0-255), - */ + /* Brightness supported is (0-255), */ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); break; case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: - /* Saturation and Contrast supported is - + /** + * Saturation and Contrast supported is - * Contrast: 0 - 255 (Default - 128) * Saturation: 0 - 255 (Default - 128) */ @@ -744,30 +769,27 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); break; case V4L2_CID_AUTOGAIN: - /* Autogain is either 0 or 1*/ - memcpy(qctrl, &tvp514x_autogain_ctrl, - sizeof(struct v4l2_queryctrl)); - err = 0; + /** + * Auto Gain supported is - + * 0 - 1 (Default - 1) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); break; default: - v4l_err(decoder->client, - "invalid control id %d\n", qctrl->id); + v4l2_err(sd, "invalid control id %d\n", qctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Query Control: %s : Min - %d, Max - %d, Def - %d", - qctrl->name, - qctrl->minimum, - qctrl->maximum, + v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d", + qctrl->name, qctrl->minimum, qctrl->maximum, qctrl->default_value); return err; } /** - * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_g_ctrl() - V4L2 decoder interface handler for g_ctrl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, returns the control's current @@ -775,9 +797,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) * supported. */ static int -ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (ctrl == NULL) return -EINVAL; @@ -811,74 +833,70 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return -EINVAL; } - v4l_dbg(1, debug, decoder->client, - "Get Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d", ctrl->id, ctrl->value); return 0; } /** - * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, sets the control's current * value in HW. Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err = -EINVAL, value; if (ctrl == NULL) return err; - value = (__s32) ctrl->value; + value = ctrl->value; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid brightness setting %d\n", + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); if (err) return err; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid contrast setting %d\n", + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_CONTRAST, - value); + err = tvp514x_write_reg(sd, REG_CONTRAST, value); if (err) return err; + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid saturation setting %d\n", + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_SATURATION, - value); + err = tvp514x_write_reg(sd, REG_SATURATION, value); if (err) return err; + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: @@ -889,15 +907,13 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0; else { - v4l_err(decoder->client, - "invalid hue setting %d\n", - ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_HUE, - value); + err = tvp514x_write_reg(sd, REG_HUE, value); if (err) return err; + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: @@ -906,41 +922,38 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0x0C; else { - v4l_err(decoder->client, - "invalid auto gain setting %d\n", + v4l2_err(sd, "invalid auto gain setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, - value); + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value); if (err) return err; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Set Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d", ctrl->id, ctrl->value); return err; } /** - * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_enum_fmt_cap() - V4L2 decoder interface handler for enum_fmt + * @sd: pointer to standard V4L2 sub-device structure * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure * * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats */ static int -ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int index; if (fmt == NULL) @@ -948,24 +961,25 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) index = fmt->index; if ((index >= decoder->num_fmts) || (index < 0)) - return -EINVAL; /* Index out of bound */ + /* Index out of bound */ + return -EINVAL; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memcpy(fmt, &decoder->fmt_list[index], sizeof(struct v4l2_fmtdesc)); - v4l_dbg(1, debug, decoder->client, - "Current FMT: index - %d (%s)", + v4l2_dbg(1, debug, sd, "Current FMT: index - %d (%s)", decoder->fmt_list[index].index, decoder->fmt_list[index].description); return 0; } /** - * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_try_fmt_cap() - V4L2 decoder interface handler for try_fmt + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure * * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This @@ -973,9 +987,9 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) * without actually making it take effect. */ static int -ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int ifmt; struct v4l2_pix_format *pix; enum tvp514x_std current_std; @@ -984,12 +998,13 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix = &f->fmt.pix; /* Calculate height and width based on current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1003,7 +1018,8 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) break; } if (ifmt == decoder->num_fmts) - ifmt = 0; /* None of the format matched, select default */ + /* None of the format matched, select default */ + ifmt = 0; pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; pix->field = V4L2_FIELD_INTERLACED; @@ -1012,8 +1028,7 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) pix->colorspace = V4L2_COLORSPACE_SMPTE170M; pix->priv = 0; - v4l_dbg(1, debug, decoder->client, - "Try FMT: pixelformat - %s, bytesperline - %d" + v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d" "Width - %d, Height - %d", decoder->fmt_list[ifmt].description, pix->bytesperline, pix->width, pix->height); @@ -1021,8 +1036,8 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_fmt_cap() - V4L2 decoder interface handler for s_fmt + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure * * If the requested format is supported, configures the HW to use that @@ -1030,9 +1045,9 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) * correctly configured. */ static int -ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_pix_format *pix; int rval; @@ -1040,10 +1055,11 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; pix = &f->fmt.pix; - rval = ioctl_try_fmt_cap(s, f); + rval = tvp514x_try_fmt_cap(sd, f); if (rval) return rval; @@ -1053,28 +1069,28 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap - * @s: pointer to standard V4L2 device structure + * tvp514x_g_fmt_cap() - V4L2 decoder interface handler for tvp514x_g_fmt_cap + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 v4l2_format structure * * Returns the decoder's current pixel format in the v4l2_format * parameter. */ static int -ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_g_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (f == NULL) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; f->fmt.pix = decoder->pix; - v4l_dbg(1, debug, decoder->client, - "Current FMT: bytesperline - %d" + v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d" "Width - %d, Height - %d", decoder->pix.bytesperline, decoder->pix.width, decoder->pix.height); @@ -1082,16 +1098,16 @@ ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure * * Returns the decoder's video CAPTURE parameters. */ static int -ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_captureparm *cparm; enum tvp514x_std current_std; @@ -1099,13 +1115,14 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1120,17 +1137,17 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } /** - * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure * * Configures the decoder to use the input parameters, if possible. If * not possible, returns the appropriate error code. */ static int -ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_fract *timeperframe; enum tvp514x_std current_std; @@ -1138,12 +1155,13 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; timeperframe = &a->parm.capture.timeperframe; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1156,111 +1174,58 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } /** - * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num - * @s: pointer to standard V4L2 device structure - * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure - * - * Gets slave interface parameters. - * Calculates the required xclk value to support the requested - * clock parameters in p. This value is returned in the p - * parameter. - */ -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tvp514x_decoder *decoder = s->priv; - int rval; - - if (p == NULL) - return -EINVAL; - - if (NULL == decoder->pdata->ifparm) - return -EINVAL; - - rval = decoder->pdata->ifparm(p); - if (rval) { - v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); - return rval; - } - - p->u.bt656.clock_curr = TVP514X_XCLK_BT656; - - return 0; -} - -/** - * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num - * @s: pointer to standard V4L2 device structure - * @p: void pointer to hold decoder's private data address - * - * Returns device's (decoder's) private data area address in p parameter - */ -static int ioctl_g_priv(struct v4l2_int_device *s, void *p) -{ - struct tvp514x_decoder *decoder = s->priv; - - if (NULL == decoder->pdata->priv_data_set) - return -EINVAL; - - return decoder->pdata->priv_data_set(p); -} - -/** - * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num - * @s: pointer to standard V4L2 device structure - * @on: power state to which device is to be set + * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable * - * Sets devices power state to requrested state, if possible. + * Sets streaming to enable or disable, if possible. */ -static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { - struct tvp514x_decoder *decoder = s->priv; int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tvp514x_decoder *decoder = to_decoder(sd); - switch (on) { - case V4L2_POWER_OFF: - /* Power Down Sequence */ - err = - tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, - 0x01); - /* Disable mux for TVP5146/47 decoder data path */ - if (decoder->pdata->power_set) - err |= decoder->pdata->power_set(on); - decoder->state = STATE_NOT_DETECTED; - break; + if (decoder->streaming == enable) + return 0; - case V4L2_POWER_STANDBY: - if (decoder->pdata->power_set) - err = decoder->pdata->power_set(on); + switch (enable) { + case 0: + { + /* Power Down Sequence */ + err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); + if (err) { + v4l2_err(sd, "Unable to turn off decoder\n"); + return err; + } + decoder->streaming = enable; break; + } + case 1: + { + struct tvp514x_reg *int_seq = (struct tvp514x_reg *) + client->driver->id_table->driver_data; - case V4L2_POWER_ON: - /* Enable mux for TVP5146/47 decoder data path */ - if ((decoder->pdata->power_set) && - (decoder->state == STATE_NOT_DETECTED)) { - int i; - struct tvp514x_init_seq *int_seq = - (struct tvp514x_init_seq *) - decoder->id->driver_data; - - err = decoder->pdata->power_set(on); - - /* Power Up Sequence */ - for (i = 0; i < int_seq->no_regs; i++) { - err |= tvp514x_write_reg(decoder->client, - int_seq->init_reg_seq[i].reg, - int_seq->init_reg_seq[i].val); - } - /* Detect the sensor is not already detected */ - err |= tvp514x_detect(decoder); - if (err) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } + /* Power Up Sequence */ + err = tvp514x_write_regs(sd, int_seq); + if (err) { + v4l2_err(sd, "Unable to turn on decoder\n"); + return err; } - err |= tvp514x_configure(decoder); + /* Detect if not already detected */ + err = tvp514x_detect(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to detect decoder\n"); + return err; + } + err = tvp514x_configure(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to configure decoder\n"); + return err; + } + decoder->streaming = enable; break; - + } default: err = -ENODEV; break; @@ -1269,93 +1234,38 @@ static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) return err; } -/** - * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT - * @s: pointer to standard V4L2 device structure - * - * Initialize the decoder device (calls tvp514x_configure()) - */ -static int ioctl_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - - /* Set default standard to auto */ - decoder->tvp514x_regs[REG_VIDEO_STD].val = - VIDEO_STD_AUTO_SWITCH_BIT; - - return tvp514x_configure(decoder); -} - -/** - * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num - * @s: pointer to standard V4L2 device structure - * - * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. - */ -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -/** - * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num - * @s: pointer to standard V4L2 device structure - * - * Initialise the device when slave attaches to the master. Returns 0 if - * TVP5146/47 device could be found, otherwise returns appropriate error. - */ -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - int err; - - err = tvp514x_detect(decoder); - if (err < 0) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } - - v4l_info(decoder->client, - "chip version 0x%.2x detected\n", decoder->ver); +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .queryctrl = tvp514x_queryctrl, + .g_ctrl = tvp514x_g_ctrl, + .s_ctrl = tvp514x_s_ctrl, + .s_std = tvp514x_s_std, +}; - return 0; -} +static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_routing = tvp514x_s_routing, + .querystd = tvp514x_querystd, + .enum_fmt = tvp514x_enum_fmt_cap, + .g_fmt = tvp514x_g_fmt_cap, + .try_fmt = tvp514x_try_fmt_cap, + .s_fmt = tvp514x_s_fmt_cap, + .g_parm = tvp514x_g_parm, + .s_parm = tvp514x_s_parm, + .s_stream = tvp514x_s_stream, +}; -static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { - {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, - {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, - {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, - {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, - {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, - {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, - {vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, - {vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, - {vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, - {vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, - {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, - {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, - {vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *) ioctl_queryctrl}, - {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, - {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, - {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, - {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, - {vidioc_int_s_video_routing_num, - (v4l2_int_ioctl_func *) ioctl_s_routing}, +static const struct v4l2_subdev_ops tvp514x_ops = { + .core = &tvp514x_core_ops, + .video = &tvp514x_video_ops, }; static struct tvp514x_decoder tvp514x_dev = { - .state = STATE_NOT_DETECTED, + .streaming = 0, .fmt_list = tvp514x_fmt_list, .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), - .pix = { /* Default to NTSC 8-bit YUV 422 */ + .pix = { + /* Default to NTSC 8-bit YUV 422 */ .width = NTSC_NUM_ACTIVE_PIXELS, .height = NTSC_NUM_ACTIVE_LINES, .pixelformat = V4L2_PIX_FMT_UYVY, @@ -1369,20 +1279,13 @@ static struct tvp514x_decoder tvp514x_dev = { .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), - .v4l2_int_device = { - .module = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - .type = v4l2_int_type_slave, - }, - .tvp514x_slave = { - .ioctls = tvp514x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), - }, + }; /** - * tvp514x_probe - decoder driver i2c probe handler + * tvp514x_probe() - decoder driver i2c probe handler * @client: i2c driver client device structure + * @id: i2c driver id table * * Register decoder as an i2c client device and V4L2 * device. @@ -1391,88 +1294,71 @@ static int tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tvp514x_decoder *decoder; - int err; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + if (!client->dev.platform_data) { + v4l2_err(client, "No platform data!!\n"); + return -ENODEV; + } + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); if (!decoder) return -ENOMEM; - if (!client->dev.platform_data) { - v4l_err(client, "No platform data!!\n"); - err = -ENODEV; - goto out_free; - } - + /* Initialize the tvp514x_decoder with default configuration */ *decoder = tvp514x_dev; - decoder->v4l2_int_device.priv = decoder; - decoder->pdata = client->dev.platform_data; - decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + /* Copy default register configuration */ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, sizeof(tvp514x_reg_list_default)); - /* + + /* Copy board specific information here */ + decoder->pdata = client->dev.platform_data; + + /** * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. */ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= - (decoder->pdata->clk_polarity << 1); + (decoder->pdata->clk_polarity << 1); decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= - ((decoder->pdata->hs_polarity << 2) | - (decoder->pdata->vs_polarity << 3)); - /* - * Save the id data, required for power up sequence - */ - decoder->id = (struct i2c_device_id *)id; - /* Attach to Master */ - strcpy(decoder->v4l2_int_device.u.slave->attach_to, - decoder->pdata->master); - decoder->client = client; - i2c_set_clientdata(client, decoder); + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; /* Register with V4L2 layer as slave device */ - err = v4l2_int_device_register(&decoder->v4l2_int_device); - if (err) { - i2c_set_clientdata(client, NULL); - v4l_err(client, - "Unable to register to v4l2. Err[%d]\n", err); - goto out_free; - - } else - v4l_info(client, "Registered to v4l2 master %s!!\n", - decoder->pdata->master); + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); + return 0; -out_free: - kfree(decoder); - return err; } /** - * tvp514x_remove - decoder driver i2c remove handler + * tvp514x_remove() - decoder driver i2c remove handler * @client: i2c driver client device structure * * Unregister decoder as an i2c client device and V4L2 * device. Complement of tvp514x_probe(). */ -static int __exit tvp514x_remove(struct i2c_client *client) +static int tvp514x_remove(struct i2c_client *client) { - struct tvp514x_decoder *decoder = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp514x_decoder *decoder = to_decoder(sd); - v4l2_int_device_unregister(&decoder->v4l2_int_device); - i2c_set_clientdata(client, NULL); + v4l2_device_unregister_subdev(sd); kfree(decoder); return 0; } -/* - * TVP5146 Init/Power on Sequence - */ +/* TVP5146 Init/Power on Sequence */ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1485,14 +1371,10 @@ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5146_init = { - .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), - .init_reg_seq = tvp5146_init_reg_seq, -}; -/* - * TVP5147 Init/Power on Sequence - */ + +/* TVP5147 Init/Power on Sequence */ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1512,71 +1394,51 @@ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5147_init = { - .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), - .init_reg_seq = tvp5147_init_reg_seq, -}; -/* - * TVP5146M2/TVP5147M1 Init/Power on Sequence - */ + +/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp514xm_init = { - .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), - .init_reg_seq = tvp514xm_init_reg_seq, -}; -/* + +/** * I2C Device Table - * * name - Name of the actual device/chip. * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (unsigned long)&tvp5146_init}, - {"tvp5146m2", (unsigned long)&tvp514xm_init}, - {"tvp5147", (unsigned long)&tvp5147_init}, - {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, + {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, + {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, + {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, {}, }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); -static struct i2c_driver tvp514x_i2c_driver = { +static struct i2c_driver tvp514x_driver = { .driver = { - .name = TVP514X_MODULE_NAME, - .owner = THIS_MODULE, - }, + .owner = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + }, .probe = tvp514x_probe, - .remove = __exit_p(tvp514x_remove), + .remove = tvp514x_remove, .id_table = tvp514x_id, }; -/** - * tvp514x_init - * - * Module init function - */ static int __init tvp514x_init(void) { - return i2c_add_driver(&tvp514x_i2c_driver); + return i2c_add_driver(&tvp514x_driver); } -/** - * tvp514x_cleanup - * - * Module exit function - */ -static void __exit tvp514x_cleanup(void) +static void __exit tvp514x_exit(void) { - i2c_del_driver(&tvp514x_i2c_driver); + i2c_del_driver(&tvp514x_driver); } module_init(tvp514x_init); -module_exit(tvp514x_cleanup); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("TVP514X linux decoder driver"); -MODULE_LICENSE("GPL"); +module_exit(tvp514x_exit); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h index 351620aeecc..18f29ad0dfe 100644 --- a/drivers/media/video/tvp514x_regs.h +++ b/drivers/media/video/tvp514x_regs.h @@ -284,14 +284,4 @@ struct tvp514x_reg { u32 val; }; -/** - * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up - * Sequence. - * @ no_regs - Number of registers to write for power up sequence. - * @ init_reg_seq - Array of registers and respective value to write. - */ -struct tvp514x_init_seq { - unsigned int no_regs; - const struct tvp514x_reg *init_reg_seq; -}; #endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index aa5065ea09e..269ab044072 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -24,7 +24,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/tw9910.h> @@ -223,9 +223,8 @@ struct tw9910_hsync_ctrl { }; struct tw9910_priv { + struct v4l2_subdev subdev; struct tw9910_video_info *info; - struct i2c_client *client; - struct soc_camera_device icd; const struct tw9910_scale_ctrl *scale; }; @@ -356,6 +355,12 @@ static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = { /* * general function */ +static struct tw9910_priv *to_tw9910(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct tw9910_priv, + subdev); +} + static int tw9910_set_scale(struct i2c_client *client, const struct tw9910_scale_ctrl *scale) { @@ -509,44 +514,20 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) /* * soc_camera_ops function */ -static int tw9910_init(struct soc_camera_device *icd) -{ - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - int ret = 0; - - if (priv->info->link.power) { - ret = priv->info->link.power(&priv->client->dev, 1); - if (ret < 0) - return ret; - } - - if (priv->info->link.reset) - ret = priv->info->link.reset(&priv->client->dev); - - return ret; -} - -static int tw9910_release(struct soc_camera_device *icd) +static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - int ret = 0; - - if (priv->info->link.power) - ret = priv->info->link.power(&priv->client->dev, 0); - - return ret; -} + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); -static int tw9910_start_capture(struct soc_camera_device *icd) -{ - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + if (!enable) + return 0; if (!priv->scale) { - dev_err(&icd->dev, "norm select error\n"); + dev_err(&client->dev, "norm select error\n"); return -EPERM; } - dev_dbg(&icd->dev, "%s %dx%d\n", + dev_dbg(&client->dev, "%s %dx%d\n", priv->scale->name, priv->scale->width, priv->scale->height); @@ -554,11 +535,6 @@ static int tw9910_start_capture(struct soc_camera_device *icd) return 0; } -static int tw9910_stop_capture(struct soc_camera_device *icd) -{ - return 0; -} - static int tw9910_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { @@ -567,8 +543,9 @@ static int tw9910_set_bus_param(struct soc_camera_device *icd, static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - struct soc_camera_link *icl = priv->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; @@ -576,21 +553,11 @@ static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int tw9910_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) -{ - id->ident = V4L2_IDENT_TW9910; - id->revision = 0; - - return 0; -} - -static int tw9910_set_std(struct soc_camera_device *icd, - v4l2_std_id *a) +static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { int ret = -EINVAL; - if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL)) + if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL)) ret = 0; return ret; @@ -606,17 +573,26 @@ static int tw9910_enum_input(struct soc_camera_device *icd, return 0; } +static int tw9910_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + id->ident = V4L2_IDENT_TW9910; + id->revision = 0; + + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG -static int tw9910_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int tw9910_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct i2c_client *client = sd->priv; int ret; if (reg->reg > 0xff) return -EINVAL; - ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + ret = i2c_smbus_read_byte_data(client, reg->reg); if (ret < 0) return ret; @@ -628,23 +604,25 @@ static int tw9910_get_register(struct soc_camera_device *icd, return 0; } -static int tw9910_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int tw9910_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct i2c_client *client = sd->priv; if (reg->reg > 0xff || reg->val > 0xff) return -EINVAL; - return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); } #endif -static int tw9910_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct v4l2_rect *rect = &a->c; + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_device *icd = client->dev.platform_data; int ret = -EINVAL; u8 val; @@ -658,8 +636,8 @@ static int tw9910_set_crop(struct soc_camera_device *icd, /* * reset hardware */ - tw9910_reset(priv->client); - ret = tw9910_write_array(priv->client, tw9910_default_regs); + tw9910_reset(client); + ret = tw9910_write_array(client, tw9910_default_regs); if (ret < 0) goto tw9910_set_fmt_error; @@ -670,7 +648,7 @@ static int tw9910_set_crop(struct soc_camera_device *icd, if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) val = LEN; - ret = tw9910_mask_set(priv->client, OPFORM, LEN, val); + ret = tw9910_mask_set(client, OPFORM, LEN, val); if (ret < 0) goto tw9910_set_fmt_error; @@ -698,52 +676,139 @@ static int tw9910_set_crop(struct soc_camera_device *icd, val = 0; } - ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val); + ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); if (ret < 0) goto tw9910_set_fmt_error; /* * set scale */ - ret = tw9910_set_scale(priv->client, priv->scale); + ret = tw9910_set_scale(client, priv->scale); if (ret < 0) goto tw9910_set_fmt_error; /* * set cropping */ - ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl); + ret = tw9910_set_cropping(client, &tw9910_cropping_ctrl); if (ret < 0) goto tw9910_set_fmt_error; /* * set hsync */ - ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl); + ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl); if (ret < 0) goto tw9910_set_fmt_error; + rect->width = priv->scale->width; + rect->height = priv->scale->height; + rect->left = 0; + rect->top = 0; + return ret; tw9910_set_fmt_error: - tw9910_reset(priv->client); + tw9910_reset(client); priv->scale = NULL; return ret; } -static int tw9910_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + + if (!priv->scale) { + int ret; + struct v4l2_crop crop = { + .c = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }; + ret = tw9910_s_crop(sd, &crop); + if (ret < 0) + return ret; + } + + a->c.left = 0; + a->c.top = 0; + a->c.width = priv->scale->width; + a->c.height = priv->scale->height; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 768; + a->bounds.height = 576; + a->defrect.left = 0; + a->defrect.top = 0; + a->defrect.width = 640; + a->defrect.height = 480; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (!priv->scale) { + int ret; + struct v4l2_crop crop = { + .c = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }; + ret = tw9910_s_crop(sd, &crop); + if (ret < 0) + return ret; + } + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix->width = priv->scale->width; + pix->height = priv->scale->height; + pix->pixelformat = V4L2_PIX_FMT_VYUY; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, + /* See tw9910_s_crop() - no proper cropping support */ + struct v4l2_crop a = { + .c = { + .left = 0, + .top = 0, + .width = pix->width, + .height = pix->height, + }, }; - int i; + int i, ret; /* * check color format @@ -755,19 +820,25 @@ static int tw9910_set_fmt(struct soc_camera_device *icd, if (i == ARRAY_SIZE(tw9910_color_fmt)) return -EINVAL; - return tw9910_set_crop(icd, &rect); + ret = tw9910_s_crop(sd, &a); + if (!ret) { + pix->width = priv->scale->width; + pix->height = priv->scale->height; + } + return ret; } -static int tw9910_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; const struct tw9910_scale_ctrl *scale; if (V4L2_FIELD_ANY == pix->field) { pix->field = V4L2_FIELD_INTERLACED; } else if (V4L2_FIELD_INTERLACED != pix->field) { - dev_err(&icd->dev, "Field type invalid.\n"); + dev_err(&client->dev, "Field type invalid.\n"); return -EINVAL; } @@ -784,11 +855,11 @@ static int tw9910_try_fmt(struct soc_camera_device *icd, return 0; } -static int tw9910_video_probe(struct soc_camera_device *icd) +static int tw9910_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct tw9910_priv *priv = to_tw9910(client); s32 val; - int ret; /* * We must have a parent by now. And it cannot be a wrong one. @@ -803,7 +874,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd) */ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&icd->dev, "bus width error\n"); + dev_err(&client->dev, "bus width error\n"); return -ENODEV; } @@ -813,54 +884,54 @@ static int tw9910_video_probe(struct soc_camera_device *icd) /* * check and show Product ID */ - val = i2c_smbus_read_byte_data(priv->client, ID); + val = i2c_smbus_read_byte_data(client, ID); + if (0x0B != GET_ID(val) || 0x00 != GET_ReV(val)) { - dev_err(&icd->dev, + dev_err(&client->dev, "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val)); return -ENODEV; } - dev_info(&icd->dev, + dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val)); - ret = soc_camera_video_start(icd); - if (ret < 0) - return ret; - icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; icd->vdev->current_norm = V4L2_STD_NTSC; - return ret; -} - -static void tw9910_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); + return 0; } static struct soc_camera_ops tw9910_ops = { - .owner = THIS_MODULE, - .probe = tw9910_video_probe, - .remove = tw9910_video_remove, - .init = tw9910_init, - .release = tw9910_release, - .start_capture = tw9910_start_capture, - .stop_capture = tw9910_stop_capture, - .set_crop = tw9910_set_crop, - .set_fmt = tw9910_set_fmt, - .try_fmt = tw9910_try_fmt, .set_bus_param = tw9910_set_bus_param, .query_bus_param = tw9910_query_bus_param, - .get_chip_id = tw9910_get_chip_id, - .set_std = tw9910_set_std, .enum_input = tw9910_enum_input, +}; + +static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { + .g_chip_ident = tw9910_g_chip_ident, + .s_std = tw9910_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = tw9910_get_register, - .set_register = tw9910_set_register, + .g_register = tw9910_g_register, + .s_register = tw9910_s_register, #endif }; +static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { + .s_stream = tw9910_s_stream, + .g_fmt = tw9910_g_fmt, + .s_fmt = tw9910_s_fmt, + .try_fmt = tw9910_try_fmt, + .cropcap = tw9910_cropcap, + .g_crop = tw9910_g_crop, + .s_crop = tw9910_s_crop, +}; + +static struct v4l2_subdev_ops tw9910_subdev_ops = { + .core = &tw9910_subdev_core_ops, + .video = &tw9910_subdev_video_ops, +}; + /* * i2c_driver function */ @@ -871,18 +942,24 @@ static int tw9910_probe(struct i2c_client *client, { struct tw9910_priv *priv; struct tw9910_video_info *info; - struct soc_camera_device *icd; - const struct tw9910_scale_ctrl *scale; - int i, ret; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = + to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "TW9910: missing soc-camera data!\n"); + return -EINVAL; + } - if (!client->dev.platform_data) + icl = to_soc_camera_link(icd); + if (!icl) return -EINVAL; - info = container_of(client->dev.platform_data, - struct tw9910_video_info, link); + info = container_of(icl, struct tw9910_video_info, link); - if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent), - I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "I2C-Adapter doesn't support " "I2C_FUNC_SMBUS_BYTE_DATA\n"); @@ -894,40 +971,15 @@ static int tw9910_probe(struct i2c_client *client, return -ENOMEM; priv->info = info; - priv->client = client; - i2c_set_clientdata(client, priv); - icd = &priv->icd; + v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); + icd->ops = &tw9910_ops; - icd->control = &client->dev; icd->iface = info->link.bus_id; - /* - * set width and height - */ - icd->width_max = tw9910_ntsc_scales[0].width; /* set default */ - icd->width_min = tw9910_ntsc_scales[0].width; - icd->height_max = tw9910_ntsc_scales[0].height; - icd->height_min = tw9910_ntsc_scales[0].height; - - scale = tw9910_ntsc_scales; - for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) { - icd->width_max = max(scale[i].width, icd->width_max); - icd->width_min = min(scale[i].width, icd->width_min); - icd->height_max = max(scale[i].height, icd->height_max); - icd->height_min = min(scale[i].height, icd->height_min); - } - scale = tw9910_pal_scales; - for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) { - icd->width_max = max(scale[i].width, icd->width_max); - icd->width_min = min(scale[i].width, icd->width_min); - icd->height_max = max(scale[i].height, icd->height_max); - icd->height_min = min(scale[i].height, icd->height_min); - } - - ret = soc_camera_device_register(icd); - + ret = tw9910_video_probe(icd, client); if (ret) { + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); } @@ -937,9 +989,10 @@ static int tw9910_probe(struct i2c_client *client, static int tw9910_remove(struct i2c_client *client) { - struct tw9910_priv *priv = i2c_get_clientdata(client); + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&priv->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); return 0; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 6ba16abeebd..e0f91e4ab65 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -28,7 +28,6 @@ #include <linux/timer.h> #include <linux/slab.h> #include <linux/mm.h> -#include <linux/utsname.h> #include <linux/highmem.h> #include <linux/vmalloc.h> #include <linux/module.h> diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 1fe5befbbf8..c19f51dba2e 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/utsname.h> #include <linux/init.h> #include <asm/uaccess.h> #include <linux/ioport.h> @@ -246,9 +245,9 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: case CODEC_SAA7111: - v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + v4l2_i2c_new_subdev(&usbvision->v4l2_dev, &usbvision->i2c_adap, "saa7115", - "saa7115_auto", saa711x_addrs); + "saa7115_auto", 0, saa711x_addrs); break; } if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { @@ -256,16 +255,16 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) enum v4l2_i2c_tuner_type type; struct tuner_setup tun_setup; - sd = v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, &usbvision->i2c_adap, "tuner", - "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); /* depending on whether we found a demod or not, select the tuner type. */ type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - sd = v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, &usbvision->i2c_adap, "tuner", - "tuner", v4l2_i2c_tuner_addrs(type)); + "tuner", 0, v4l2_i2c_tuner_addrs(type)); if (usbvision->tuner_type != -1) { tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 90d9b5c0e9a..a2a50d608a3 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -52,7 +52,6 @@ #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/mm.h> -#include <linux/utsname.h> #include <linux/highmem.h> #include <linux/vmalloc.h> #include <linux/module.h> diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 36a6ba92df2..c3225a56174 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -34,7 +34,7 @@ static struct uvc_control_info uvc_ctrls[] = { { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BRIGHTNESS_CONTROL, + .selector = UVC_PU_BRIGHTNESS_CONTROL, .index = 0, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -42,7 +42,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_CONTRAST_CONTROL, + .selector = UVC_PU_CONTRAST_CONTROL, .index = 1, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -50,7 +50,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_CONTROL, + .selector = UVC_PU_HUE_CONTROL, .index = 2, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -58,7 +58,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SATURATION_CONTROL, + .selector = UVC_PU_SATURATION_CONTROL, .index = 3, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -66,7 +66,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SHARPNESS_CONTROL, + .selector = UVC_PU_SHARPNESS_CONTROL, .index = 4, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -74,7 +74,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAMMA_CONTROL, + .selector = UVC_PU_GAMMA_CONTROL, .index = 5, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -82,7 +82,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, .index = 6, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -90,7 +90,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .index = 7, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -98,7 +98,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, .index = 8, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -106,7 +106,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAIN_CONTROL, + .selector = UVC_PU_GAIN_CONTROL, .index = 9, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -114,7 +114,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, .index = 10, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -122,7 +122,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_AUTO_CONTROL, + .selector = UVC_PU_HUE_AUTO_CONTROL, .index = 11, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -130,7 +130,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, .index = 12, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -138,7 +138,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, .index = 13, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -146,7 +146,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_DIGITAL_MULTIPLIER_CONTROL, + .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL, .index = 14, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -154,7 +154,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, + .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, .index = 15, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -162,21 +162,21 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_ANALOG_VIDEO_STANDARD_CONTROL, + .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL, .index = 16, .size = 1, .flags = UVC_CONTROL_GET_CUR, }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_ANALOG_LOCK_STATUS_CONTROL, + .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL, .index = 17, .size = 1, .flags = UVC_CONTROL_GET_CUR, }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_SCANNING_MODE_CONTROL, + .selector = UVC_CT_SCANNING_MODE_CONTROL, .index = 0, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -184,7 +184,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_MODE_CONTROL, + .selector = UVC_CT_AE_MODE_CONTROL, .index = 1, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -193,7 +193,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_PRIORITY_CONTROL, + .selector = UVC_CT_AE_PRIORITY_CONTROL, .index = 2, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -201,7 +201,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, .index = 3, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -209,7 +209,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_RELATIVE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, .index = 4, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -217,7 +217,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, .index = 5, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -225,7 +225,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_RELATIVE_CONTROL, + .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, .index = 6, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -233,7 +233,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_IRIS_ABSOLUTE_CONTROL, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, .index = 7, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -241,7 +241,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_IRIS_RELATIVE_CONTROL, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, .index = 8, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -249,7 +249,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, .index = 9, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -257,7 +257,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_RELATIVE_CONTROL, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .index = 10, .size = 3, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -265,7 +265,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PANTILT_ABSOLUTE_CONTROL, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, .index = 11, .size = 8, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -273,7 +273,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PANTILT_RELATIVE_CONTROL, + .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -281,7 +281,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ROLL_ABSOLUTE_CONTROL, + .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL, .index = 13, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -289,7 +289,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ROLL_RELATIVE_CONTROL, + .selector = UVC_CT_ROLL_RELATIVE_CONTROL, .index = 14, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -297,7 +297,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_AUTO_CONTROL, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, .index = 17, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -305,7 +305,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PRIVACY_CONTROL, + .selector = UVC_CT_PRIVACY_CONTROL, .index = 18, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -332,13 +332,13 @@ static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, __s8 zoom = (__s8)data[0]; switch (query) { - case GET_CUR: + case UVC_GET_CUR: return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); - case GET_MIN: - case GET_MAX: - case GET_RES: - case GET_DEF: + case UVC_GET_MIN: + case UVC_GET_MAX: + case UVC_GET_RES: + case UVC_GET_DEF: default: return data[2]; } @@ -356,7 +356,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BRIGHTNESS, .name = "Brightness", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BRIGHTNESS_CONTROL, + .selector = UVC_PU_BRIGHTNESS_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -366,7 +366,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_CONTRAST, .name = "Contrast", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_CONTRAST_CONTROL, + .selector = UVC_PU_CONTRAST_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -376,7 +376,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_HUE, .name = "Hue", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_CONTROL, + .selector = UVC_PU_HUE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -386,7 +386,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_SATURATION, .name = "Saturation", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SATURATION_CONTROL, + .selector = UVC_PU_SATURATION_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -396,7 +396,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_SHARPNESS, .name = "Sharpness", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SHARPNESS_CONTROL, + .selector = UVC_PU_SHARPNESS_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -406,7 +406,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_GAMMA, .name = "Gamma", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAMMA_CONTROL, + .selector = UVC_PU_GAMMA_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -416,7 +416,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BACKLIGHT_COMPENSATION, .name = "Backlight Compensation", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -426,7 +426,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_GAIN, .name = "Gain", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAIN_CONTROL, + .selector = UVC_PU_GAIN_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -436,7 +436,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .name = "Power Line Frequency", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, .size = 2, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, @@ -448,7 +448,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_HUE_AUTO, .name = "Hue, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_AUTO_CONTROL, + .selector = UVC_PU_HUE_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -458,7 +458,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_AUTO, .name = "Exposure, Auto", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_MODE_CONTROL, + .selector = UVC_CT_AE_MODE_CONTROL, .size = 4, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, @@ -470,7 +470,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, .name = "Exposure, Auto Priority", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_PRIORITY_CONTROL, + .selector = UVC_CT_AE_PRIORITY_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -480,7 +480,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_ABSOLUTE, .name = "Exposure (Absolute)", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, .size = 32, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -490,7 +490,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_AUTO_WHITE_BALANCE, .name = "White Balance Temperature, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -500,7 +500,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, .name = "White Balance Temperature", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -510,7 +510,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_AUTO_WHITE_BALANCE, .name = "White Balance Component, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -520,7 +520,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BLUE_BALANCE, .name = "White Balance Blue Component", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -530,7 +530,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_RED_BALANCE, .name = "White Balance Red Component", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .size = 16, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -540,7 +540,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_FOCUS_ABSOLUTE, .name = "Focus (absolute)", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -550,7 +550,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_FOCUS_AUTO, .name = "Focus, Auto", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_AUTO_CONTROL, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -560,7 +560,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_ZOOM_ABSOLUTE, .name = "Zoom, Absolute", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -570,7 +570,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_ZOOM_CONTINUOUS, .name = "Zoom, Continuous", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_RELATIVE_CONTROL, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .size = 0, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -582,7 +582,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PRIVACY_CONTROL, + .selector = UVC_CT_PRIVACY_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -675,16 +675,16 @@ static const __u8 uvc_media_transport_input_guid[16] = static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) { switch (UVC_ENTITY_TYPE(entity)) { - case ITT_CAMERA: + case UVC_ITT_CAMERA: return memcmp(uvc_camera_guid, guid, 16) == 0; - case ITT_MEDIA_TRANSPORT_INPUT: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: return memcmp(uvc_processing_guid, guid, 16) == 0; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: return memcmp(entity->extension.guidExtensionCode, guid, 16) == 0; @@ -729,7 +729,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, } } -struct uvc_control *uvc_find_control(struct uvc_video_device *video, +struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping) { struct uvc_control *ctrl = NULL; @@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; - list_for_each_entry(entity, &video->iterms, chain) { + list_for_each_entry(entity, &chain->iterms, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; } - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, return ctrl; } -int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl) { struct uvc_control *ctrl; @@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, __u8 *data; int ret; - ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); if (ctrl == NULL) return -EINVAL; @@ -793,11 +793,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { - if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); + v4l2_ctrl->default_value = + mapping->get(mapping, UVC_GET_DEF, data); } switch (mapping->v4l2_type) { @@ -829,25 +831,28 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, } if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { - if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { - if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); + v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { - if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); + v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data); } ret = 0; @@ -881,9 +886,9 @@ out: * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the * control lock. */ -int uvc_ctrl_begin(struct uvc_video_device *video) +int uvc_ctrl_begin(struct uvc_video_chain *chain) { - return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; + return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; } static int uvc_ctrl_commit_entity(struct uvc_device *dev, @@ -912,7 +917,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, continue; if (!rollback) - ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, + ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id, dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); @@ -933,34 +938,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) { struct uvc_entity *entity; int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); if (ret < 0) goto done; - list_for_each_entry(entity, &video->iterms, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->iterms, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } - list_for_each_entry(entity, &video->extensions, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->extensions, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } done: - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } -int uvc_ctrl_get(struct uvc_video_device *video, +int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -969,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video, unsigned int i; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { - ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if (ret < 0) @@ -984,7 +989,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = mapping->get(mapping, GET_CUR, + xctrl->value = mapping->get(mapping, UVC_GET_CUR, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { @@ -1000,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, return 0; } -int uvc_ctrl_set(struct uvc_video_device *video, +int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -1008,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, s32 value = xctrl->value; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) return -EINVAL; @@ -1023,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 0, ctrl->info->size); } else { - ret = uvc_query_ctrl(video->dev, GET_CUR, - ctrl->entity->id, video->dev->intfnum, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); @@ -1053,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, * Dynamic controls */ -int uvc_xu_ctrl_query(struct uvc_video_device *video, +int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *xctrl, int set) { struct uvc_entity *entity; @@ -1063,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { if (entity->id == xctrl->unit) break; } @@ -1102,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) return -EINVAL; - if (mutex_lock_interruptible(&video->ctrl_mutex)) + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), @@ -1115,9 +1120,9 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, goto out; } - ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, - video->dev->intfnum, xctrl->selector, data, - xctrl->size); + ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, + xctrl->unit, chain->dev->intfnum, xctrl->selector, + data, xctrl->size); if (ret < 0) goto out; @@ -1132,7 +1137,7 @@ out: uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), xctrl->size); - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } @@ -1211,7 +1216,7 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, if (!found) return; - if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { /* Check if the device control information and length match * the user supplied information. */ @@ -1219,8 +1224,9 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, __le16 size; __u8 inf; - if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { + ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, + dev->intfnum, info->selector, (__u8 *)&size, 2); + if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " "control " UVC_GUID_FORMAT "/%u (%d).\n", UVC_GUID_ARGS(info->entity), info->selector, @@ -1236,8 +1242,9 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, return; } - if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1)) < 0) { + ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, &inf, 1); + if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " "control " UVC_GUID_FORMAT "/%u (%d).\n", UVC_GUID_ARGS(info->entity), info->selector, @@ -1391,7 +1398,7 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) unsigned int size; unsigned int i; - if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + if (UVC_ENTITY_TYPE(entity) != UVC_VC_PROCESSING_UNIT) return; controls = entity->processing.bmControls; @@ -1427,13 +1434,13 @@ int uvc_ctrl_init_device(struct uvc_device *dev) unsigned int bControlSize = 0, ncontrols = 0; __u8 *bmControls = NULL; - if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { bmControls = entity->extension.bmControls; bControlSize = entity->extension.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { + } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) { bmControls = entity->processing.bmControls; bControlSize = entity->processing.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { + } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) { bmControls = entity->camera.bmControls; bControlSize = entity->camera.bControlSize; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 89927b7aec2..8756be56915 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -249,23 +249,23 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, list_for_each_entry_continue(entity, &dev->entities, list) { switch (UVC_ENTITY_TYPE(entity)) { - case TT_STREAMING: + case UVC_TT_STREAMING: if (entity->output.bSourceID == id) return entity; break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: if (entity->processing.bSourceID == id) return entity; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: for (i = 0; i < entity->selector.bNrInPins; ++i) if (entity->selector.baSourceID[i] == id) return entity; break; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: for (i = 0; i < entity->extension.bNrInPins; ++i) if (entity->extension.baSourceID[i] == id) return entity; @@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, return NULL; } +static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTerminalLink == id) + return stream; + } + + return NULL; +} + /* ------------------------------------------------------------------------ - * Descriptors handling + * Descriptors parsing */ static int uvc_parse_format(struct uvc_device *dev, @@ -297,9 +309,9 @@ static int uvc_parse_format(struct uvc_device *dev, format->index = buffer[3]; switch (buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_FRAME_BASED: - n = buffer[2] == VS_FORMAT_UNCOMPRESSED ? 27 : 28; + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_FRAME_BASED: + n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; if (buflen < n) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -325,16 +337,16 @@ static int uvc_parse_format(struct uvc_device *dev, } format->bpp = buffer[21]; - if (buffer[2] == VS_FORMAT_UNCOMPRESSED) { - ftype = VS_FRAME_UNCOMPRESSED; + if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { + ftype = UVC_VS_FRAME_UNCOMPRESSED; } else { - ftype = VS_FRAME_FRAME_BASED; + ftype = UVC_VS_FRAME_FRAME_BASED; if (buffer[27]) format->flags = UVC_FMT_FLAG_COMPRESSED; } break; - case VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_MJPEG: if (buflen < 11) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -347,10 +359,10 @@ static int uvc_parse_format(struct uvc_device *dev, format->fcc = V4L2_PIX_FMT_MJPEG; format->flags = UVC_FMT_FLAG_COMPRESSED; format->bpp = 0; - ftype = VS_FRAME_MJPEG; + ftype = UVC_VS_FRAME_MJPEG; break; - case VS_FORMAT_DV: + case UVC_VS_FORMAT_DV: if (buflen < 9) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -395,8 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, format->nframes = 1; break; - case VS_FORMAT_MPEG2TS: - case VS_FORMAT_STREAM_BASED: + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: /* Not supported yet. */ default: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " @@ -416,7 +428,7 @@ static int uvc_parse_format(struct uvc_device *dev, */ while (buflen > 2 && buffer[2] == ftype) { frame = &format->frame[format->nframes]; - if (ftype != VS_FRAME_FRAME_BASED) + if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else n = buflen > 21 ? buffer[21] : 0; @@ -436,7 +448,7 @@ static int uvc_parse_format(struct uvc_device *dev, frame->wHeight = get_unaligned_le16(&buffer[7]); frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); - if (ftype != VS_FRAME_FRAME_BASED) { + if (ftype != UVC_VS_FRAME_FRAME_BASED) { frame->dwMaxVideoFrameBufferSize = get_unaligned_le32(&buffer[17]); frame->dwDefaultFrameInterval = @@ -491,12 +503,12 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { + if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -530,7 +542,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, int ret = -EINVAL; if (intf->cur_altsetting->desc.bInterfaceSubClass - != SC_VIDEOSTREAMING) { + != UVC_SC_VIDEOSTREAMING) { uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " "video streaming interface\n", dev->udev->devnum, intf->altsetting[0].desc.bInterfaceNumber); @@ -551,6 +563,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, } mutex_init(&streaming->mutex); + streaming->dev = dev; streaming->intf = usb_get_intf(intf); streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; @@ -589,12 +602,12 @@ static int uvc_parse_streaming(struct uvc_device *dev, /* Parse the header descriptor. */ switch (buffer[2]) { - case VS_OUTPUT_HEADER: + case UVC_VS_OUTPUT_HEADER: streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; size = 9; break; - case VS_INPUT_HEADER: + case UVC_VS_INPUT_HEADER: streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; size = 13; break; @@ -618,7 +631,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->header.bNumFormats = p; streaming->header.bEndpointAddress = buffer[6]; - if (buffer[2] == VS_INPUT_HEADER) { + if (buffer[2] == UVC_VS_INPUT_HEADER) { streaming->header.bmInfo = buffer[7]; streaming->header.bTerminalLink = buffer[8]; streaming->header.bStillCaptureMethod = buffer[9]; @@ -644,15 +657,15 @@ static int uvc_parse_streaming(struct uvc_device *dev, _buflen = buflen; /* Count the format and frame descriptors. */ - while (_buflen > 2 && _buffer[1] == CS_INTERFACE) { + while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { switch (_buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_MJPEG: - case VS_FORMAT_FRAME_BASED: + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_FRAME_BASED: nformats++; break; - case VS_FORMAT_DV: + case UVC_VS_FORMAT_DV: /* DV format has no frame descriptor. We will create a * dummy frame descriptor with a dummy frame interval. */ @@ -661,22 +674,22 @@ static int uvc_parse_streaming(struct uvc_device *dev, nintervals++; break; - case VS_FORMAT_MPEG2TS: - case VS_FORMAT_STREAM_BASED: + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT %u is not supported.\n", dev->udev->devnum, alts->desc.bInterfaceNumber, _buffer[2]); break; - case VS_FRAME_UNCOMPRESSED: - case VS_FRAME_MJPEG: + case UVC_VS_FRAME_UNCOMPRESSED: + case UVC_VS_FRAME_MJPEG: nframes++; if (_buflen > 25) nintervals += _buffer[25] ? _buffer[25] : 3; break; - case VS_FRAME_FRAME_BASED: + case UVC_VS_FRAME_FRAME_BASED: nframes++; if (_buflen > 21) nintervals += _buffer[21] ? _buffer[21] : 3; @@ -709,12 +722,12 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->nformats = nformats; /* Parse the format descriptors. */ - while (buflen > 2 && buffer[1] == CS_INTERFACE) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { switch (buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_MJPEG: - case VS_FORMAT_DV: - case VS_FORMAT_FRAME_BASED: + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_DV: + case UVC_VS_FORMAT_FRAME_BASED: format->frame = frame; ret = uvc_parse_format(dev, streaming, format, &interval, buffer, buflen); @@ -751,7 +764,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->maxpsize = psize; } - list_add_tail(&streaming->list, &dev->streaming); + list_add_tail(&streaming->list, &dev->streams); return 0; error: @@ -819,7 +832,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, return -ENOMEM; unit->id = buffer[3]; - unit->type = VC_EXTENSION_UNIT; + unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); @@ -856,7 +869,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, __u16 type; switch (buffer[2]) { - case VC_HEADER: + case UVC_VC_HEADER: n = buflen >= 12 ? buffer[11] : 0; if (buflen < 12 || buflen < 12 + n) { @@ -883,7 +896,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, } break; - case VC_INPUT_TERMINAL: + case UVC_VC_INPUT_TERMINAL: if (buflen < 8) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL error\n", @@ -908,11 +921,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = 0; len = 8; - if (type == ITT_CAMERA) { + if (type == UVC_ITT_CAMERA) { n = buflen >= 15 ? buffer[14] : 0; len = 15; - } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { + } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { n = buflen >= 9 ? buffer[8] : 0; p = buflen >= 10 + n ? buffer[9+n] : 0; len = 10; @@ -932,7 +945,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->id = buffer[3]; term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { + if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = @@ -942,7 +955,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->camera.wOcularFocalLength = get_unaligned_le16(&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n); - } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { + } else if (UVC_ENTITY_TYPE(term) == + UVC_ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; term->media.bmControls = (__u8 *)term + sizeof *term; term->media.bTransportModeSize = p; @@ -955,9 +969,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (buffer[7] != 0) usb_string(udev, buffer[7], term->name, sizeof term->name); - else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) sprintf(term->name, "Camera %u", buffer[3]); - else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) sprintf(term->name, "Media %u", buffer[3]); else sprintf(term->name, "Input %u", buffer[3]); @@ -965,7 +979,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&term->list, &dev->entities); break; - case VC_OUTPUT_TERMINAL: + case UVC_VC_OUTPUT_TERMINAL: if (buflen < 9) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d OUTPUT_TERMINAL error\n", @@ -1002,7 +1016,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&term->list, &dev->entities); break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: p = buflen >= 5 ? buffer[4] : 0; if (buflen < 5 || buflen < 6 + p) { @@ -1031,7 +1045,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&unit->list, &dev->entities); break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: n = buflen >= 8 ? buffer[7] : 0; p = dev->uvc_version >= 0x0110 ? 10 : 9; @@ -1066,7 +1080,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&unit->list, &dev->entities); break; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: p = buflen >= 22 ? buffer[21] : 0; n = buflen >= 24 + p ? buffer[22+p] : 0; @@ -1158,43 +1172,40 @@ next_descriptor: } /* ------------------------------------------------------------------------ - * USB probe and disconnect + * UVC device scan */ /* - * Unregister the video devices. - */ -static void uvc_unregister_video(struct uvc_device *dev) -{ - if (dev->video.vdev) { - if (dev->video.vdev->minor == -1) - video_device_release(dev->video.vdev); - else - video_unregister_device(dev->video.vdev); - dev->video.vdev = NULL; - } -} - -/* * Scan the UVC descriptors to locate a chain starting at an Output Terminal * and containing the following units: * - * - one Output Terminal (USB Streaming or Display) + * - one or more Output Terminals (USB Streaming or Display) * - zero or one Processing Unit - * - zero, one or mode single-input Selector Units + * - zero, one or more single-input Selector Units * - zero or one multiple-input Selector Units, provided all inputs are * connected to input terminals * - zero, one or mode single-input Extension Units * - one or more Input Terminals (Camera, External or USB Streaming) * - * A side forward scan is made on each detected entity to check for additional - * extension units. + * The terminal and units must match on of the following structures: + * + * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) + * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... + * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) + * + * +---------+ +---------+ -> OTT_*(0) + * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... + * +---------+ +---------+ -> OTT_*(n) + * + * The Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. Single-input Selector Units are ignored. */ -static int uvc_scan_chain_entity(struct uvc_video_device *video, +static int uvc_scan_chain_entity(struct uvc_video_chain *chain, struct uvc_entity *entity) { switch (UVC_ENTITY_TYPE(entity)) { - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); @@ -1204,23 +1215,23 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - list_add_tail(&entity->chain, &video->extensions); + list_add_tail(&entity->chain, &chain->extensions); break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- PU %d", entity->id); - if (video->processing != NULL) { + if (chain->processing != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple " "Processing Units in chain.\n"); return -1; } - video->processing = entity; + chain->processing = entity; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- SU %d", entity->id); @@ -1228,25 +1239,25 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, if (entity->selector.bNrInPins == 1) break; - if (video->selector != NULL) { + if (chain->selector != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " "Units in chain.\n"); return -1; } - video->selector = entity; + chain->selector = entity; break; - case ITT_VENDOR_SPECIFIC: - case ITT_CAMERA: - case ITT_MEDIA_TRANSPORT_INPUT: + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &video->iterms); + list_add_tail(&entity->chain, &chain->iterms); break; - case TT_STREAMING: + case UVC_TT_STREAMING: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); @@ -1256,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - if (video->sterm != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming " - "entities in chain.\n"); - return -1; - } - - list_add_tail(&entity->chain, &video->iterms); - video->sterm = entity; + list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1275,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_forward(struct uvc_video_device *video, +static int uvc_scan_chain_forward(struct uvc_video_chain *chain, struct uvc_entity *entity, struct uvc_entity *prev) { struct uvc_entity *forward; @@ -1286,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, found = 0; while (1) { - forward = uvc_entity_by_reference(video->dev, entity->id, + forward = uvc_entity_by_reference(chain->dev, entity->id, forward); if (forward == NULL) break; - - if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT || - forward == prev) + if (forward == prev) continue; - if (forward->extension.bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has " - "more than 1 input pin.\n", entity->id); - return -1; - } + switch (UVC_ENTITY_TYPE(forward)) { + case UVC_VC_EXTENSION_UNIT: + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " + "has more than 1 input pin.\n", + entity->id); + return -EINVAL; + } + + list_add_tail(&forward->chain, &chain->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); + + printk(" XU %d", forward->id); + found = 1; + } + break; + + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + if (UVC_ENTITY_IS_ITERM(forward)) { + uvc_trace(UVC_TRACE_DESCR, "Unsupported input " + "terminal %u.\n", forward->id); + return -EINVAL; + } - list_add_tail(&forward->chain, &video->extensions); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(" (-> XU"); + list_add_tail(&forward->chain, &chain->oterms); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); - printk(" %d", forward->id); - found = 1; + printk(" OT %d", forward->id); + found = 1; + } + break; } } if (found) @@ -1316,22 +1343,22 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_backward(struct uvc_video_device *video, +static int uvc_scan_chain_backward(struct uvc_video_chain *chain, struct uvc_entity *entity) { struct uvc_entity *term; int id = -1, i; switch (UVC_ENTITY_TYPE(entity)) { - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: id = entity->extension.baSourceID[0]; break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: id = entity->processing.bSourceID; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ if (entity->selector.bNrInPins == 1) { id = entity->selector.baSourceID[0]; @@ -1341,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT"); - video->selector = entity; + chain->selector = entity; for (i = 0; i < entity->selector.bNrInPins; ++i) { id = entity->selector.baSourceID[i]; - term = uvc_entity_by_id(video->dev, id); + term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " "input %d isn't connected to an " @@ -1355,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &video->iterms); - uvc_scan_chain_forward(video, term, entity); + list_add_tail(&term->chain, &chain->iterms); + uvc_scan_chain_forward(chain, term, entity); } if (uvc_trace_param & UVC_TRACE_PROBE) @@ -1369,125 +1396,170 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, return id; } -static int uvc_scan_chain(struct uvc_video_device *video) +static int uvc_scan_chain(struct uvc_video_chain *chain, + struct uvc_entity *oterm) { struct uvc_entity *entity, *prev; int id; - entity = video->oterm; + entity = oterm; + list_add_tail(&entity->chain, &chain->oterms); uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); - if (UVC_ENTITY_TYPE(entity) == TT_STREAMING) - video->sterm = entity; - id = entity->output.bSourceID; while (id != 0) { prev = entity; - entity = uvc_entity_by_id(video->dev, id); + entity = uvc_entity_by_id(chain->dev, id); if (entity == NULL) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " "unknown entity %d.\n", id); - return -1; + return -EINVAL; + } + + if (entity->chain.next || entity->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", id); + return -EINVAL; } /* Process entity */ - if (uvc_scan_chain_entity(video, entity) < 0) - return -1; + if (uvc_scan_chain_entity(chain, entity) < 0) + return -EINVAL; /* Forward scan */ - if (uvc_scan_chain_forward(video, entity, prev) < 0) - return -1; + if (uvc_scan_chain_forward(chain, entity, prev) < 0) + return -EINVAL; /* Stop when a terminal is found. */ - if (!UVC_ENTITY_IS_UNIT(entity)) + if (UVC_ENTITY_IS_TERM(entity)) break; /* Backward scan */ - id = uvc_scan_chain_backward(video, entity); + id = uvc_scan_chain_backward(chain, entity); if (id < 0) return id; } - if (video->sterm == NULL) { - uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in " - "chain.\n"); - return -1; + return 0; +} + +static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +{ + struct uvc_entity *term; + unsigned int nterms = 0; + char *p = buffer; + + list_for_each_entry(term, terms, chain) { + p += sprintf(p, "%u", term->id); + if (term->chain.next != terms) { + p += sprintf(p, ","); + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; + } + } } - return 0; + return p - buffer; +} + +static const char *uvc_print_chain(struct uvc_video_chain *chain) +{ + static char buffer[43]; + char *p = buffer; + + p += uvc_print_terms(&chain->iterms, p); + p += sprintf(p, " -> "); + uvc_print_terms(&chain->oterms, p); + + return buffer; } /* - * Register the video devices. - * - * The driver currently supports a single video device per control interface - * only. The terminal and units must match the following structure: + * Scan the device for video chains and register video devices. * - * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING - * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_* - * - * The Extension Units, if present, must have a single input pin. The - * Processing Unit and Extension Units can be in any order. Additional - * Extension Units connected to the main chain as single-unit branches are - * also supported. + * Chains are scanned starting at their output terminals and walked backwards. */ -static int uvc_register_video(struct uvc_device *dev) +static int uvc_scan_device(struct uvc_device *dev) { - struct video_device *vdev; + struct uvc_video_chain *chain; struct uvc_entity *term; - int found = 0, ret; - /* Check if the control interface matches the structure we expect. */ list_for_each_entry(term, &dev->entities, list) { - struct uvc_streaming *streaming; - - if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) + if (!UVC_ENTITY_IS_OTERM(term)) continue; - memset(&dev->video, 0, sizeof dev->video); - mutex_init(&dev->video.ctrl_mutex); - INIT_LIST_HEAD(&dev->video.iterms); - INIT_LIST_HEAD(&dev->video.extensions); - dev->video.oterm = term; - dev->video.dev = dev; - if (uvc_scan_chain(&dev->video) < 0) + /* If the terminal is already included in a chain, skip it. + * This can happen for chains that have multiple output + * terminals, where all output terminals beside the first one + * will be inserted in the chain in forward scans. + */ + if (term->chain.next || term->chain.prev) continue; - list_for_each_entry(streaming, &dev->streaming, list) { - if (streaming->header.bTerminalLink == - dev->video.sterm->id) { - dev->video.streaming = streaming; - found = 1; - break; - } + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&chain->iterms); + INIT_LIST_HEAD(&chain->oterms); + INIT_LIST_HEAD(&chain->extensions); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + + if (uvc_scan_chain(chain, term) < 0) { + kfree(chain); + continue; } - if (found) - break; + uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", + uvc_print_chain(chain)); + + list_add_tail(&chain->list, &dev->chains); } - if (!found) { + if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; } - if (uvc_trace_param & UVC_TRACE_PROBE) { - uvc_printk(KERN_INFO, "Found a valid video chain ("); - list_for_each_entry(term, &dev->video.iterms, chain) { - printk("%d", term->id); - if (term->chain.next != &dev->video.iterms) - printk(","); - } - printk(" -> %d).\n", dev->video.oterm->id); + return 0; +} + +/* ------------------------------------------------------------------------ + * Video device registration and unregistration + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->vdev == NULL) + continue; + + if (stream->vdev->minor == -1) + video_device_release(stream->vdev); + else + video_unregister_device(stream->vdev); + stream->vdev = NULL; } +} - /* Initialize the video buffers queue. */ - uvc_queue_init(&dev->video.queue, dev->video.streaming->type); +static int uvc_register_video(struct uvc_device *dev, + struct uvc_streaming *stream) +{ + struct video_device *vdev; + int ret; /* Initialize the streaming interface with default streaming * parameters. */ - if ((ret = uvc_video_init(&dev->video)) < 0) { + ret = uvc_video_init(stream); + if (ret < 0) { uvc_printk(KERN_ERR, "Failed to initialize the device " "(%d).\n", ret); return ret; @@ -1495,8 +1567,11 @@ static int uvc_register_video(struct uvc_device *dev) /* Register the device with V4L. */ vdev = video_device_alloc(); - if (vdev == NULL) - return -1; + if (vdev == NULL) { + uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", + ret); + return -ENOMEM; + } /* We already hold a reference to dev->udev. The video device will be * unregistered before the reference is released, so we don't need to @@ -1511,18 +1586,73 @@ static int uvc_register_video(struct uvc_device *dev) /* Set the driver data before calling video_register_device, otherwise * uvc_v4l2_open might race us. */ - dev->video.vdev = vdev; - video_set_drvdata(vdev, &dev->video); - - if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { - dev->video.vdev = NULL; + stream->vdev = vdev; + video_set_drvdata(vdev, stream); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", + ret); + stream->vdev = NULL; video_device_release(vdev); - return -1; + return ret; + } + + return 0; +} + +/* + * Register all video devices in all chains. + */ +static int uvc_register_terms(struct uvc_device *dev, + struct uvc_video_chain *chain, struct list_head *terms) +{ + struct uvc_streaming *stream; + struct uvc_entity *term; + int ret; + + list_for_each_entry(term, terms, chain) { + if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) + continue; + + stream = uvc_stream_by_id(dev, term->id); + if (stream == NULL) { + uvc_printk(KERN_INFO, "No streaming interface found " + "for terminal %u.", term->id); + continue; + } + + stream->chain = chain; + ret = uvc_register_video(dev, stream); + if (ret < 0) + return ret; + } + + return 0; +} + +static int uvc_register_chains(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + int ret; + + list_for_each_entry(chain, &dev->chains, list) { + ret = uvc_register_terms(dev, chain, &chain->iterms); + if (ret < 0) + return ret; + + ret = uvc_register_terms(dev, chain, &chain->oterms); + if (ret < 0) + return ret; } return 0; } +/* ------------------------------------------------------------------------ + * USB probe, disconnect, suspend and resume + */ + /* * Delete the UVC device. * @@ -1544,7 +1674,7 @@ void uvc_delete(struct kref *kref) struct uvc_device *dev = container_of(kref, struct uvc_device, kref); struct list_head *p, *n; - /* Unregister the video device. */ + /* Unregister the video devices. */ uvc_unregister_video(dev); usb_put_intf(dev->intf); usb_put_dev(dev->udev); @@ -1552,13 +1682,19 @@ void uvc_delete(struct kref *kref) uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; entity = list_entry(p, struct uvc_entity, list); kfree(entity); } - list_for_each_safe(p, n, &dev->streaming) { + list_for_each_safe(p, n, &dev->streams) { struct uvc_streaming *streaming; streaming = list_entry(p, struct uvc_streaming, list); usb_driver_release_interface(&uvc_driver.driver, @@ -1592,7 +1728,8 @@ static int uvc_probe(struct usb_interface *intf, return -ENOMEM; INIT_LIST_HEAD(&dev->entities); - INIT_LIST_HEAD(&dev->streaming); + INIT_LIST_HEAD(&dev->chains); + INIT_LIST_HEAD(&dev->streams); kref_init(&dev->kref); atomic_set(&dev->users, 0); @@ -1633,8 +1770,12 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_ctrl_init_device(dev) < 0) goto error; - /* Register the video devices. */ - if (uvc_register_video(dev) < 0) + /* Scan the device for video chains. */ + if (uvc_scan_device(dev) < 0) + goto error; + + /* Register video devices. */ + if (uvc_register_chains(dev) < 0) goto error; /* Save our data pointer in the interface data. */ @@ -1664,7 +1805,8 @@ static void uvc_disconnect(struct usb_interface *intf) */ usb_set_intfdata(intf, NULL); - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING) + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOSTREAMING) return; /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide @@ -1687,31 +1829,36 @@ static void uvc_disconnect(struct usb_interface *intf) static int uvc_suspend(struct usb_interface *intf, pm_message_t message) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); /* Controls are cached on the fly so they don't need to be saved. */ - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) return uvc_status_suspend(dev); - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_suspend(stream); } - return uvc_video_suspend(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) { if (reset) { int ret = uvc_ctrl_resume_device(dev); @@ -1722,13 +1869,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset) return uvc_status_resume(dev); } - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_resume(stream); } - return uvc_video_resume(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int uvc_resume(struct usb_interface *intf) @@ -1845,11 +1993,29 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* ViMicro */ - { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + /* ViMicro Vega */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x332d, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3410, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro Venus - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, - .idProduct = 0x0000, + .idProduct = 0x3420, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, @@ -1862,7 +2028,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_DEF }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1925,7 +2092,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_EXTRAFIELDS }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c index 436f462685a..a9285b570db 100644 --- a/drivers/media/video/uvc/uvc_isight.c +++ b/drivers/media/video/uvc/uvc_isight.c @@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, return 0; } -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf) { int ret, i; @@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, * processes the data of the first payload of the new frame. */ do { - ret = isight_decode(&video->queue, buf, + ret = isight_decode(&stream->queue, buf, urb->transfer_buffer + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); @@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); } } diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c index f152a990386..1ca6dff7361 100644 --- a/drivers/media/video/uvc/uvc_status.c +++ b/drivers/media/video/uvc/uvc_status.c @@ -145,8 +145,8 @@ static void uvc_status_complete(struct urb *urb) break; default: - uvc_printk(KERN_INFO, "unknown event type %u.\n", - dev->status[0]); + uvc_trace(UVC_TRACE_STATUS, "Unknown status event " + "type %u.\n", dev->status[0]); break; } } diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 5e77cad2969..9e7351569b5 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -40,7 +40,7 @@ * table for the controls that can be mapped directly, and handle the others * manually. */ -static int uvc_v4l2_query_menu(struct uvc_video_device *video, +static int uvc_v4l2_query_menu(struct uvc_video_chain *chain, struct v4l2_querymenu *query_menu) { struct uvc_menu_info *menu_info; @@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, u32 index = query_menu->index; u32 id = query_menu->id; - ctrl = uvc_find_control(video, query_menu->id, &mapping); + ctrl = uvc_find_control(chain, query_menu->id, &mapping); if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) return -EINVAL; @@ -103,7 +103,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } -static int uvc_v4l2_try_format(struct uvc_video_device *video, +static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) { @@ -116,7 +116,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, int ret = 0; __u8 *fcc; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; fcc = (__u8 *)&fmt->fmt.pix.pixelformat; @@ -126,8 +126,8 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format. */ - for (i = 0; i < video->streaming->nformats; ++i) { - format = &video->streaming->format[i]; + for (i = 0; i < stream->nformats; ++i) { + format = &stream->format[i]; if (format->fcc == fmt->fmt.pix.pixelformat) break; } @@ -191,12 +191,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, * developers test their webcams with the Linux driver as well as with * the Windows driver). */ - if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) probe->dwMaxVideoFrameSize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; /* Probe the device. */ - if ((ret = uvc_probe_video(video, probe)) < 0) + ret = uvc_probe_video(stream, probe); + if (ret < 0) goto done; fmt->fmt.pix.width = frame->wWidth; @@ -216,13 +217,13 @@ done: return ret; } -static int uvc_v4l2_get_format(struct uvc_video_device *video, +static int uvc_v4l2_get_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { - struct uvc_format *format = video->streaming->cur_format; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_format *format = stream->cur_format; + struct uvc_frame *frame = stream->cur_frame; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; if (format == NULL || frame == NULL) @@ -233,14 +234,14 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video, fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; - fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; return 0; } -static int uvc_v4l2_set_format(struct uvc_video_device *video, +static int uvc_v4l2_set_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { struct uvc_streaming_control probe; @@ -248,39 +249,39 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, struct uvc_frame *frame; int ret; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; - if (uvc_queue_allocated(&video->queue)) + if (uvc_queue_allocated(&stream->queue)) return -EBUSY; - ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; + memcpy(&stream->ctrl, &probe, sizeof probe); + stream->cur_format = format; + stream->cur_frame = frame; return 0; } -static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { uint32_t numerator, denominator; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - numerator = video->streaming->ctrl.dwFrameInterval; + numerator = stream->ctrl.dwFrameInterval; denominator = 10000000; uvc_simplify_fraction(&numerator, &denominator, 8, 333); memset(parm, 0, sizeof *parm); - parm->type = video->streaming->type; + parm->type = stream->type; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.capturemode = 0; parm->parm.capture.timeperframe.numerator = numerator; @@ -297,19 +298,19 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, return 0; } -static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; struct uvc_streaming_control probe; struct v4l2_fract timeperframe; uint32_t interval; int ret; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - if (uvc_queue_streaming(&video->queue)) + if (uvc_queue_streaming(&stream->queue)) return -EBUSY; if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -317,7 +318,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, else timeperframe = parm->parm.output.timeperframe; - memcpy(&probe, &video->streaming->ctrl, sizeof probe); + memcpy(&probe, &stream->ctrl, sizeof probe); interval = uvc_fraction_to_interval(timeperframe.numerator, timeperframe.denominator); @@ -326,10 +327,11 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device with the new settings. */ - if ((ret = uvc_probe_video(video, &probe)) < 0) + ret = uvc_probe_video(stream, &probe); + if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); + memcpy(&stream->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ timeperframe.numerator = probe.dwFrameInterval; @@ -382,8 +384,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle) /* Check if the device already has a privileged handle. */ mutex_lock(&uvc_driver.open_mutex); - if (atomic_inc_return(&handle->device->active) != 1) { - atomic_dec(&handle->device->active); + if (atomic_inc_return(&handle->stream->active) != 1) { + atomic_dec(&handle->stream->active); ret = -EBUSY; goto done; } @@ -398,7 +400,7 @@ done: static void uvc_dismiss_privileges(struct uvc_fh *handle) { if (handle->state == UVC_HANDLE_ACTIVE) - atomic_dec(&handle->device->active); + atomic_dec(&handle->stream->active); handle->state = UVC_HANDLE_PASSIVE; } @@ -414,45 +416,47 @@ static int uvc_has_privileges(struct uvc_fh *handle) static int uvc_v4l2_open(struct file *file) { - struct uvc_video_device *video; + struct uvc_streaming *stream; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); mutex_lock(&uvc_driver.open_mutex); - video = video_drvdata(file); + stream = video_drvdata(file); - if (video->dev->state & UVC_DEV_DISCONNECTED) { + if (stream->dev->state & UVC_DEV_DISCONNECTED) { ret = -ENODEV; goto done; } - ret = usb_autopm_get_interface(video->dev->intf); + ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) goto done; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { - usb_autopm_put_interface(video->dev->intf); + usb_autopm_put_interface(stream->dev->intf); ret = -ENOMEM; goto done; } - if (atomic_inc_return(&video->dev->users) == 1) { - if ((ret = uvc_status_start(video->dev)) < 0) { - usb_autopm_put_interface(video->dev->intf); - atomic_dec(&video->dev->users); + if (atomic_inc_return(&stream->dev->users) == 1) { + ret = uvc_status_start(stream->dev); + if (ret < 0) { + usb_autopm_put_interface(stream->dev->intf); + atomic_dec(&stream->dev->users); kfree(handle); goto done; } } - handle->device = video; + handle->chain = stream->chain; + handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&video->dev->kref); + kref_get(&stream->dev->kref); done: mutex_unlock(&uvc_driver.open_mutex); @@ -461,20 +465,20 @@ done: static int uvc_v4l2_release(struct file *file) { - struct uvc_video_device *video = video_drvdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) { - uvc_video_enable(video, 0); + uvc_video_enable(stream, 0); - mutex_lock(&video->queue.mutex); - if (uvc_free_buffers(&video->queue) < 0) + mutex_lock(&stream->queue.mutex); + if (uvc_free_buffers(&stream->queue) < 0) uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " "free buffers.\n"); - mutex_unlock(&video->queue.mutex); + mutex_unlock(&stream->queue.mutex); } /* Release the file handle. */ @@ -482,19 +486,20 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; - if (atomic_dec_return(&video->dev->users) == 0) - uvc_status_stop(video->dev); + if (atomic_dec_return(&stream->dev->users) == 0) + uvc_status_stop(stream->dev); - usb_autopm_put_interface(video->dev->intf); - kref_put(&video->dev->kref, uvc_delete); + usb_autopm_put_interface(stream->dev->intf); + kref_put(&stream->dev->kref, uvc_delete); return 0; } static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_video_chain *chain = handle->chain; + struct uvc_streaming *stream = handle->stream; long ret = 0; switch (cmd) { @@ -506,10 +511,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(cap, 0, sizeof *cap); strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); strlcpy(cap->card, vdev->name, sizeof cap->card); - usb_make_path(video->dev->udev, + usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = DRIVER_VERSION_NUMBER; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else @@ -520,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get, Set & Query control */ case VIDIOC_QUERYCTRL: - return uvc_query_v4l2_ctrl(video, arg); + return uvc_query_v4l2_ctrl(chain, arg); case VIDIOC_G_CTRL: { @@ -530,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&xctrl, 0, sizeof xctrl); xctrl.id = ctrl->id; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_get(video, &xctrl); - uvc_ctrl_rollback(video); + ret = uvc_ctrl_get(chain, &xctrl); + uvc_ctrl_rollback(chain); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -550,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) xctrl.id = ctrl->id; xctrl.value = ctrl->value; - ret = uvc_ctrl_begin(video); - if (ret < 0) + uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_set(video, &xctrl); + ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); return ret; } - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); break; } case VIDIOC_QUERYMENU: - return uvc_v4l2_query_menu(video, arg); + return uvc_v4l2_query_menu(chain, arg); case VIDIOC_G_EXT_CTRLS: { @@ -572,20 +577,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_get(video, ctrl); + ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } @@ -596,14 +601,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(chain); if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(video, ctrl); + ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } @@ -612,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); else - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } /* Get, Set & Enum input */ case VIDIOC_ENUMINPUT: { - const struct uvc_entity *selector = video->selector; + const struct uvc_entity *selector = chain->selector; struct v4l2_input *input = arg; struct uvc_entity *iterm = NULL; u32 index = input->index; int pin = 0; if (selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&video->iterms, + iterm = list_first_entry(&chain->iterms, struct uvc_entity, chain); pin = iterm->id; } else if (pin < selector->selector.bNrInPins) { pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, video->iterms.next, chain) { + list_for_each_entry(iterm, chain->iterms.next, chain) { if (iterm->id == pin) break; } @@ -648,7 +653,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(input, 0, sizeof *input); input->index = index; strlcpy(input->name, iterm->name, sizeof input->name); - if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) + if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA) input->type = V4L2_INPUT_TYPE_CAMERA; break; } @@ -657,15 +662,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { u8 input; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { *(int *)arg = 0; break; } - ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, - video->dev->intfnum, SU_INPUT_SELECT_CONTROL, - &input, 1); + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); if (ret < 0) return ret; @@ -680,19 +685,19 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (input != 1) return -EINVAL; break; } - if (input == 0 || input > video->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->selector.bNrInPins) return -EINVAL; - return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, - video->dev->intfnum, SU_INPUT_SELECT_CONTROL, - &input, 1); + return uvc_query_ctrl(chain->dev, UVC_SET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); } /* Try, Get, Set & Enum format */ @@ -703,15 +708,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) enum v4l2_buf_type type = fmt->type; __u32 index = fmt->index; - if (fmt->type != video->streaming->type || - fmt->index >= video->streaming->nformats) + if (fmt->type != stream->type || + fmt->index >= stream->nformats) return -EINVAL; memset(fmt, 0, sizeof(*fmt)); fmt->index = index; fmt->type = type; - format = &video->streaming->format[fmt->index]; + format = &stream->format[fmt->index]; fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; @@ -729,17 +734,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } case VIDIOC_S_FMT: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_format(video, arg); + return uvc_v4l2_set_format(stream, arg); case VIDIOC_G_FMT: - return uvc_v4l2_get_format(video, arg); + return uvc_v4l2_get_format(stream, arg); /* Frame size enumeration */ case VIDIOC_ENUM_FRAMESIZES: @@ -750,10 +755,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fsize->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -779,10 +784,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format and frame size */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fival->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -832,21 +837,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get & Set streaming parameters */ case VIDIOC_G_PARM: - return uvc_v4l2_get_streamparm(video, arg); + return uvc_v4l2_get_streamparm(stream, arg); case VIDIOC_S_PARM: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_streamparm(video, arg); + return uvc_v4l2_set_streamparm(stream, arg); /* Cropping and scaling */ case VIDIOC_CROPCAP: { struct v4l2_cropcap *ccap = arg; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; - if (ccap->type != video->streaming->type) + if (ccap->type != stream->type) return -EINVAL; ccap->bounds.left = 0; @@ -870,16 +875,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_requestbuffers *rb = arg; unsigned int bufsize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; - if (rb->type != video->streaming->type || + if (rb->type != stream->type || rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + ret = uvc_alloc_buffers(&stream->queue, rb->count, bufsize); if (ret < 0) return ret; @@ -892,39 +897,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_buffer *buf = arg; - if (buf->type != video->streaming->type) + if (buf->type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_query_buffer(&video->queue, buf); + return uvc_query_buffer(&stream->queue, buf); } case VIDIOC_QBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_queue_buffer(&video->queue, arg); + return uvc_queue_buffer(&stream->queue, arg); case VIDIOC_DQBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_dequeue_buffer(&video->queue, arg, + return uvc_dequeue_buffer(&stream->queue, arg, file->f_flags & O_NONBLOCK); case VIDIOC_STREAMON: { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - if ((ret = uvc_video_enable(video, 1)) < 0) + ret = uvc_video_enable(stream, 1); + if (ret < 0) return ret; break; } @@ -933,13 +939,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_video_enable(video, 0); + return uvc_video_enable(stream, 0); } /* Analog video standards make no sense for digital cameras. */ @@ -1013,10 +1019,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) } case UVCIOC_CTRL_GET: - return uvc_xu_ctrl_query(video, arg, 0); + return uvc_xu_ctrl_query(chain, arg, 0); case UVCIOC_CTRL_SET: - return uvc_xu_ctrl_query(video, arg, 1); + return uvc_xu_ctrl_query(chain, arg, 1); default: if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, @@ -1070,7 +1076,9 @@ static struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; @@ -1082,15 +1090,15 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) start = vma->vm_start; size = vma->vm_end - vma->vm_start; - mutex_lock(&video->queue.mutex); + mutex_lock(&queue->mutex); - for (i = 0; i < video->queue.count; ++i) { - buffer = &video->queue.buffer[i]; + for (i = 0; i < queue->count; ++i) { + buffer = &queue->buffer[i]; if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } - if (i == video->queue.count || size != video->queue.buf_size) { + if (i == queue->count || size != queue->buf_size) { ret = -EINVAL; goto done; } @@ -1101,7 +1109,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) */ vma->vm_flags |= VM_IO; - addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + addr = (unsigned long)queue->mem + buffer->buf.m.offset; while (size > 0) { page = vmalloc_to_page((void *)addr); if ((ret = vm_insert_page(vma, start, page)) < 0) @@ -1117,17 +1125,18 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) uvc_vm_open(vma); done: - mutex_unlock(&video->queue.mutex); + mutex_unlock(&queue->mutex); return ret; } static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); - return uvc_queue_poll(&video->queue, file, wait); + return uvc_queue_poll(&stream->queue, file, wait); } const struct v4l2_file_operations uvc_fops = { diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 01b633c7348..f960e8ea4f1 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -static void uvc_fixup_video_ctrl(struct uvc_video_device *video, +static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { struct uvc_format *format; @@ -69,10 +69,10 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, unsigned int i; if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > video->streaming->nformats) + ctrl->bFormatIndex > stream->nformats) return; - format = &video->streaming->format[ctrl->bFormatIndex - 1]; + format = &stream->format[ctrl->bFormatIndex - 1]; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { @@ -86,12 +86,12 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || (ctrl->dwMaxVideoFrameSize == 0 && - video->dev->uvc_version < 0x0110)) + stream->dev->uvc_version < 0x0110)) ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; - if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && - video->streaming->intf->num_altsetting > 1) { + if (stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + stream->intf->num_altsetting > 1) { u32 interval; u32 bandwidth; @@ -108,7 +108,7 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; bandwidth *= 10000000 / interval + 1; bandwidth /= 1000; - if (video->dev->udev->speed == USB_SPEED_HIGH) + if (stream->dev->udev->speed == USB_SPEED_HIGH) bandwidth /= 8; bandwidth += 12; @@ -116,40 +116,44 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, } } -static int uvc_get_video_ctrl(struct uvc_video_device *video, +static int uvc_get_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe, __u8 query) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; + if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && + query == UVC_GET_DEF) + return -EIO; + data = kmalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; - ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, - probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, - UVC_CTRL_STREAMING_TIMEOUT); + ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, UVC_CTRL_STREAMING_TIMEOUT); - if ((query == GET_MIN || query == GET_MAX) && ret == 2) { + if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, * answer a GET_MIN or GET_MAX request with the wCompQuality * field only. */ - uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " "compliance - GET_MIN/MAX(PROBE) incorrectly " "supported. Enabling workaround.\n"); memset(ctrl, 0, sizeof ctrl); ctrl->wCompQuality = le16_to_cpup((__le16 *)data); ret = 0; goto out; - } else if (query == GET_DEF && probe == 1 && ret != size) { + } else if (query == UVC_GET_DEF && probe == 1 && ret != size) { /* Many cameras don't support the GET_DEF request on their * video probe control. Warn once and return, the caller will * fall back to GET_CUR. */ - uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non " "compliance - GET_DEF(PROBE) not supported. " "Enabling workaround.\n"); ret = -EIO; @@ -181,7 +185,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->bMinVersion = data[32]; ctrl->bMaxVersion = data[33]; } else { - ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->dwClockFrequency = stream->dev->clock_frequency; ctrl->bmFramingInfo = 0; ctrl->bPreferedVersion = 0; ctrl->bMinVersion = 0; @@ -192,7 +196,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * dwMaxPayloadTransferSize fields. Try to get the value from the * format and frame descriptors. */ - uvc_fixup_video_ctrl(video, ctrl); + uvc_fixup_video_ctrl(stream, ctrl); ret = 0; out: @@ -200,14 +204,14 @@ out: return ret; } -static int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kzalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -232,10 +236,9 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, data[33] = ctrl->bMaxVersion; } - ret = __uvc_query_ctrl(video->dev, SET_CUR, 0, - video->streaming->intfnum, - probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, - UVC_CTRL_STREAMING_TIMEOUT); + ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, UVC_CTRL_STREAMING_TIMEOUT); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -247,7 +250,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, return ret; } -int uvc_probe_video(struct uvc_video_device *video, +int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { struct uvc_streaming_control probe_min, probe_max; @@ -255,7 +258,7 @@ int uvc_probe_video(struct uvc_video_device *video, unsigned int i; int ret; - mutex_lock(&video->streaming->mutex); + mutex_lock(&stream->mutex); /* Perform probing. The device should adjust the requested values * according to its capabilities. However, some devices, namely the @@ -264,15 +267,16 @@ int uvc_probe_video(struct uvc_video_device *video, * that reason, if the needed bandwidth exceeds the maximum available * bandwidth, try to lower the quality. */ - if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) goto done; /* Get the minimum and maximum values for compression settings. */ - if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { - ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN); + if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN); if (ret < 0) goto done; - ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX); + ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX); if (ret < 0) goto done; @@ -280,18 +284,21 @@ int uvc_probe_video(struct uvc_video_device *video, } for (i = 0; i < 2; ++i) { - if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 || - (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) goto done; - if (video->streaming->intf->num_altsetting == 1) + if (stream->intf->num_altsetting == 1) break; bandwidth = probe->dwMaxPayloadTransferSize; - if (bandwidth <= video->streaming->maxpsize) + if (bandwidth <= stream->maxpsize) break; - if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { ret = -ENOSPC; goto done; } @@ -304,14 +311,14 @@ int uvc_probe_video(struct uvc_video_device *video, } done: - mutex_unlock(&video->streaming->mutex); + mutex_unlock(&stream->mutex); return ret; } -int uvc_commit_video(struct uvc_video_device *video, +int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { - return uvc_set_video_ctrl(video, probe, 0); + return uvc_set_video_ctrl(stream, probe, 0); } /* ------------------------------------------------------------------------ @@ -363,7 +370,7 @@ int uvc_commit_video(struct uvc_video_device *video, * to be called with a NULL buf parameter. uvc_video_decode_data and * uvc_video_decode_end will never be called with a NULL buffer. */ -static int uvc_video_decode_start(struct uvc_video_device *video, +static int uvc_video_decode_start(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { __u8 fid; @@ -389,25 +396,25 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * NULL. */ if (buf == NULL) { - video->last_fid = fid; + stream->last_fid = fid; return -ENODATA; } /* Synchronize to the input stream by waiting for the FID bit to be * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. * - * If the device doesn't toggle the FID bit, invert video->last_fid + * If the device doesn't toggle the FID bit, invert stream->last_fid * when the EOF bit is set to force synchronisation on the next packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { - if (fid == video->last_fid) { + if (fid == stream->last_fid) { uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " "sync).\n"); - if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && (data[1] & UVC_STREAM_EOF)) - video->last_fid ^= UVC_STREAM_FID; + stream->last_fid ^= UVC_STREAM_FID; return -ENODATA; } @@ -422,7 +429,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * last payload can be lost anyway). We thus must check if the FID has * been toggled. * - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will never trigger an end of frame detection. * * Empty buffers (bytesused == 0) don't trigger end of frame detection @@ -430,22 +437,22 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * avoids detecting end of frame conditions at FID toggling if the * previous payload had the EOF bit set. */ - if (fid != video->last_fid && buf->buf.bytesused != 0) { + if (fid != stream->last_fid && buf->buf.bytesused != 0) { uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " "toggled).\n"); buf->state = UVC_BUF_STATE_DONE; return -EAGAIN; } - video->last_fid = fid; + stream->last_fid = fid; return data[0]; } -static void uvc_video_decode_data(struct uvc_video_device *video, +static void uvc_video_decode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int maxlen, nbytes; void *mem; @@ -466,7 +473,7 @@ static void uvc_video_decode_data(struct uvc_video_device *video, } } -static void uvc_video_decode_end(struct uvc_video_device *video, +static void uvc_video_decode_end(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { /* Mark the buffer as done if the EOF marker is set. */ @@ -475,8 +482,8 @@ static void uvc_video_decode_end(struct uvc_video_device *video, if (data[0] == len) uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); buf->state = UVC_BUF_STATE_DONE; - if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) - video->last_fid ^= UVC_STREAM_FID; + if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + stream->last_fid ^= UVC_STREAM_FID; } } @@ -491,26 +498,26 @@ static void uvc_video_decode_end(struct uvc_video_device *video, * uvc_video_encode_data is called for every URB and copies the data from the * video buffer to the transfer buffer. */ -static int uvc_video_encode_header(struct uvc_video_device *video, +static int uvc_video_encode_header(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { data[0] = 2; /* Header length */ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF - | (video->last_fid & UVC_STREAM_FID); + | (stream->last_fid & UVC_STREAM_FID); return 2; } -static int uvc_video_encode_data(struct uvc_video_device *video, +static int uvc_video_encode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int nbytes; void *mem; /* Copy video data to the URB buffer. */ mem = queue->mem + buf->buf.m.offset + queue->buf_used; nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); - nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size, + nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size, nbytes); memcpy(data, mem, nbytes); @@ -526,8 +533,8 @@ static int uvc_video_encode_data(struct uvc_video_device *video, /* * Completion handler for video URBs. */ -static void uvc_video_decode_isoc(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int ret, i; @@ -542,31 +549,32 @@ static void uvc_video_decode_isoc(struct urb *urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(video, buf, mem, + ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); if (ret < 0) continue; /* Decode the payload data. */ - uvc_video_decode_data(video, buf, mem + ret, + uvc_video_decode_data(stream, buf, mem + ret, urb->iso_frame_desc[i].actual_length - ret); /* Process the header again. */ - uvc_video_decode_end(video, buf, mem, + uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, buf); } } -static void uvc_video_decode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int len, ret; @@ -576,24 +584,25 @@ static void uvc_video_decode_bulk(struct urb *urb, mem = urb->transfer_buffer; len = urb->actual_length; - video->bulk.payload_size += len; + stream->bulk.payload_size += len; /* If the URB is the first of its payload, decode and save the * header. */ - if (video->bulk.header_size == 0 && !video->bulk.skip_payload) { + if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(video, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, mem, len); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); /* If an error occured skip the rest of the payload. */ if (ret < 0 || buf == NULL) { - video->bulk.skip_payload = 1; + stream->bulk.skip_payload = 1; } else { - memcpy(video->bulk.header, mem, ret); - video->bulk.header_size = ret; + memcpy(stream->bulk.header, mem, ret); + stream->bulk.header_size = ret; mem += ret; len -= ret; @@ -606,33 +615,34 @@ static void uvc_video_decode_bulk(struct urb *urb, */ /* Process video data. */ - if (!video->bulk.skip_payload && buf != NULL) - uvc_video_decode_data(video, buf, mem, len); + if (!stream->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(stream, buf, mem, len); /* Detect the payload end by a URB smaller than the maximum size (or * a payload size equal to the maximum) and process the header again. */ if (urb->actual_length < urb->transfer_buffer_length || - video->bulk.payload_size >= video->bulk.max_payload_size) { - if (!video->bulk.skip_payload && buf != NULL) { - uvc_video_decode_end(video, buf, video->bulk.header, - video->bulk.payload_size); + stream->bulk.payload_size >= stream->bulk.max_payload_size) { + if (!stream->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(stream, buf, stream->bulk.header, + stream->bulk.payload_size); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; } } -static void uvc_video_encode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem = urb->transfer_buffer; - int len = video->urb_size, ret; + int len = stream->urb_size, ret; if (buf == NULL) { urb->transfer_buffer_length = 0; @@ -640,40 +650,40 @@ static void uvc_video_encode_bulk(struct urb *urb, } /* If the URB is the first of its payload, add the header. */ - if (video->bulk.header_size == 0) { - ret = uvc_video_encode_header(video, buf, mem, len); - video->bulk.header_size = ret; - video->bulk.payload_size += ret; + if (stream->bulk.header_size == 0) { + ret = uvc_video_encode_header(stream, buf, mem, len); + stream->bulk.header_size = ret; + stream->bulk.payload_size += ret; mem += ret; len -= ret; } /* Process video data. */ - ret = uvc_video_encode_data(video, buf, mem, len); + ret = uvc_video_encode_data(stream, buf, mem, len); - video->bulk.payload_size += ret; + stream->bulk.payload_size += ret; len -= ret; - if (buf->buf.bytesused == video->queue.buf_used || - video->bulk.payload_size == video->bulk.max_payload_size) { - if (buf->buf.bytesused == video->queue.buf_used) { - video->queue.buf_used = 0; + if (buf->buf.bytesused == stream->queue.buf_used || + stream->bulk.payload_size == stream->bulk.max_payload_size) { + if (buf->buf.bytesused == stream->queue.buf_used) { + stream->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); - video->last_fid ^= UVC_STREAM_FID; + uvc_queue_next_buffer(&stream->queue, buf); + stream->last_fid ^= UVC_STREAM_FID; } - video->bulk.header_size = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.payload_size = 0; } - urb->transfer_buffer_length = video->urb_size - len; + urb->transfer_buffer_length = stream->urb_size - len; } static void uvc_video_complete(struct urb *urb) { - struct uvc_video_device *video = urb->context; - struct uvc_video_queue *queue = &video->queue; + struct uvc_streaming *stream = urb->context; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *buf = NULL; unsigned long flags; int ret; @@ -687,7 +697,7 @@ static void uvc_video_complete(struct urb *urb) "completion handler.\n", urb->status); case -ENOENT: /* usb_kill_urb() called. */ - if (video->frozen) + if (stream->frozen) return; case -ECONNRESET: /* usb_unlink_urb() called. */ @@ -702,7 +712,7 @@ static void uvc_video_complete(struct urb *urb) queue); spin_unlock_irqrestore(&queue->irqlock, flags); - video->decode(urb, video, buf); + stream->decode(urb, stream, buf); if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", @@ -713,19 +723,19 @@ static void uvc_video_complete(struct urb *urb) /* * Free transfer buffers. */ -static void uvc_free_urb_buffers(struct uvc_video_device *video) +static void uvc_free_urb_buffers(struct uvc_streaming *stream) { unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if (video->urb_buffer[i]) { - usb_buffer_free(video->dev->udev, video->urb_size, - video->urb_buffer[i], video->urb_dma[i]); - video->urb_buffer[i] = NULL; + if (stream->urb_buffer[i]) { + usb_buffer_free(stream->dev->udev, stream->urb_size, + stream->urb_buffer[i], stream->urb_dma[i]); + stream->urb_buffer[i] = NULL; } } - video->urb_size = 0; + stream->urb_size = 0; } /* @@ -739,15 +749,15 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video) * * Return the number of allocated packets on success or 0 when out of memory. */ -static int uvc_alloc_urb_buffers(struct uvc_video_device *video, +static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, unsigned int size, unsigned int psize, gfp_t gfp_flags) { unsigned int npackets; unsigned int i; /* Buffers are already allocated, bail out. */ - if (video->urb_size) - return video->urb_size / psize; + if (stream->urb_size) + return stream->urb_size / psize; /* Compute the number of packets. Bulk endpoints might transfer UVC * payloads accross multiple URBs. @@ -759,17 +769,17 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { - video->urb_buffer[i] = usb_buffer_alloc( - video->dev->udev, psize * npackets, - gfp_flags | __GFP_NOWARN, &video->urb_dma[i]); - if (!video->urb_buffer[i]) { - uvc_free_urb_buffers(video); + stream->urb_buffer[i] = usb_buffer_alloc( + stream->dev->udev, psize * npackets, + gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); + if (!stream->urb_buffer[i]) { + uvc_free_urb_buffers(stream); break; } } if (i == UVC_URBS) { - video->urb_size = psize * npackets; + stream->urb_size = psize * npackets; return npackets; } } @@ -780,29 +790,30 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* * Uninitialize isochronous/bulk URBs and free transfer buffers. */ -static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers) +static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) { struct urb *urb; unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if ((urb = video->urb[i]) == NULL) + urb = stream->urb[i]; + if (urb == NULL) continue; usb_kill_urb(urb); usb_free_urb(urb); - video->urb[i] = NULL; + stream->urb[i] = NULL; } if (free_buffers) - uvc_free_urb_buffers(video); + uvc_free_urb_buffers(stream); } /* * Initialize isochronous URBs and allocate transfer buffers. The packet size * is given by the endpoint. */ -static int uvc_init_video_isoc(struct uvc_video_device *video, +static int uvc_init_video_isoc(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -812,9 +823,9 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - size = video->streaming->ctrl.dwMaxVideoFrameSize; + size = stream->ctrl.dwMaxVideoFrameSize; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; @@ -823,18 +834,18 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(npackets, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - urb->dev = video->dev->udev; - urb->context = video; - urb->pipe = usb_rcvisocpipe(video->dev->udev, + urb->dev = stream->dev->udev; + urb->context = stream; + urb->pipe = usb_rcvisocpipe(stream->dev->udev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; - urb->transfer_buffer = video->urb_buffer[i]; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_buffer = stream->urb_buffer[i]; + urb->transfer_dma = stream->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; @@ -844,7 +855,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, urb->iso_frame_desc[j].length = psize; } - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -854,7 +865,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, * Initialize bulk URBs and allocate transfer buffers. The packet size is * given by the endpoint. */ -static int uvc_init_video_bulk(struct uvc_video_device *video, +static int uvc_init_video_bulk(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -863,39 +874,39 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, u32 size; psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; - size = video->streaming->ctrl.dwMaxPayloadTransferSize; - video->bulk.max_payload_size = size; + size = stream->ctrl.dwMaxPayloadTransferSize; + stream->bulk.max_payload_size = size; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; size = npackets * psize; if (usb_endpoint_dir_in(&ep->desc)) - pipe = usb_rcvbulkpipe(video->dev->udev, + pipe = usb_rcvbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); else - pipe = usb_sndbulkpipe(video->dev->udev, + pipe = usb_sndbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) size = 0; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(0, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - usb_fill_bulk_urb(urb, video->dev->udev, pipe, - video->urb_buffer[i], size, uvc_video_complete, - video); + usb_fill_bulk_urb(urb, stream->dev->udev, pipe, + stream->urb_buffer[i], size, uvc_video_complete, + stream); urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_dma = stream->urb_dma[i]; - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -904,35 +915,35 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, /* * Initialize isochronous/bulk URBs and allocate transfer buffers. */ -static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) +static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) { - struct usb_interface *intf = video->streaming->intf; + struct usb_interface *intf = stream->intf; struct usb_host_interface *alts; struct usb_host_endpoint *ep = NULL; - int intfnum = video->streaming->intfnum; + int intfnum = stream->intfnum; unsigned int bandwidth, psize, i; int ret; - video->last_fid = -1; - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->last_fid = -1; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; if (intf->num_altsetting > 1) { /* Isochronous endpoint, select the alternate setting. */ - bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { uvc_printk(KERN_WARNING, "device %s requested null " "bandwidth, defaulting to lowest.\n", - video->vdev->name); + stream->dev->name); bandwidth = 1; } for (i = 0; i < intf->num_altsetting; ++i) { alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) continue; @@ -946,18 +957,19 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) if (i >= intf->num_altsetting) return -EIO; - if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + ret = usb_set_interface(stream->dev->udev, intfnum, i); + if (ret < 0) return ret; - ret = uvc_init_video_isoc(video, ep, gfp_flags); + ret = uvc_init_video_isoc(stream, ep, gfp_flags); } else { /* Bulk endpoint, proceed to URB initialization. */ ep = uvc_find_endpoint(&intf->altsetting[0], - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) return -EIO; - ret = uvc_init_video_bulk(video, ep, gfp_flags); + ret = uvc_init_video_bulk(stream, ep, gfp_flags); } if (ret < 0) @@ -965,10 +977,11 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) /* Submit the URBs. */ for (i = 0; i < UVC_URBS; ++i) { - if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) { + ret = usb_submit_urb(stream->urb[i], gfp_flags); + if (ret < 0) { uvc_printk(KERN_ERR, "Failed to submit URB %u " "(%d).\n", i, ret); - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return ret; } } @@ -987,14 +1000,14 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) * video buffers in any way. We mark the device as frozen to make sure the URB * completion handler won't try to cancel the queue when we kill the URBs. */ -int uvc_video_suspend(struct uvc_video_device *video) +int uvc_video_suspend(struct uvc_streaming *stream) { - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - video->frozen = 1; - uvc_uninit_video(video, 0); - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + stream->frozen = 1; + uvc_uninit_video(stream, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); return 0; } @@ -1006,22 +1019,24 @@ int uvc_video_suspend(struct uvc_video_device *video) * buffers, making sure userspace applications are notified of the problem * instead of waiting forever. */ -int uvc_video_resume(struct uvc_video_device *video) +int uvc_video_resume(struct uvc_streaming *stream) { int ret; - video->frozen = 0; + stream->frozen = 0; - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { - uvc_queue_enable(&video->queue, 0); + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) { + uvc_queue_enable(&stream->queue, 0); return ret; } - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - if ((ret = uvc_init_video(video, GFP_NOIO)) < 0) - uvc_queue_enable(&video->queue, 0); + ret = uvc_init_video(stream, GFP_NOIO); + if (ret < 0) + uvc_queue_enable(&stream->queue, 0); return ret; } @@ -1040,47 +1055,53 @@ int uvc_video_resume(struct uvc_video_device *video) * * This function is called before registering the device with V4L. */ -int uvc_video_init(struct uvc_video_device *video) +int uvc_video_init(struct uvc_streaming *stream) { - struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_streaming_control *probe = &stream->ctrl; struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; int ret; - if (video->streaming->nformats == 0) { + if (stream->nformats == 0) { uvc_printk(KERN_INFO, "No supported video formats found.\n"); return -EINVAL; } + atomic_set(&stream->active, 0); + + /* Initialize the video buffers queue. */ + uvc_queue_init(&stream->queue, stream->type); + /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if * they don't receive a SET_INTERFACE request before any other video * control request. */ - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); /* Set the streaming probe control with default streaming parameters * retrieved from the device. Webcams that don't suport GET_DEF * requests on the probe control will just keep their current streaming * parameters. */ - if (uvc_get_video_ctrl(video, probe, 1, GET_DEF) == 0) - uvc_set_video_ctrl(video, probe, 1); + if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) + uvc_set_video_ctrl(stream, probe, 1); /* Initialize the streaming parameters with the probe control current * value. This makes sure SET_CUR requests on the streaming commit * control will always use values retrieved from a successful GET_CUR * request on the probe control, as required by the UVC specification. */ - if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) return ret; /* Check if the default format descriptor exists. Use the first * available format otherwise. */ - for (i = video->streaming->nformats; i > 0; --i) { - format = &video->streaming->format[i-1]; + for (i = stream->nformats; i > 0; --i) { + format = &stream->format[i-1]; if (format->index == probe->bFormatIndex) break; } @@ -1105,21 +1126,20 @@ int uvc_video_init(struct uvc_video_device *video) probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; - atomic_set(&video->active, 0); + stream->cur_format = format; + stream->cur_frame = frame; /* Select the video decoding function */ - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) - video->decode = uvc_video_decode_isight; - else if (video->streaming->intf->num_altsetting > 1) - video->decode = uvc_video_decode_isoc; + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + stream->decode = uvc_video_decode_isight; + else if (stream->intf->num_altsetting > 1) + stream->decode = uvc_video_decode_isoc; else - video->decode = uvc_video_decode_bulk; + stream->decode = uvc_video_decode_bulk; } else { - if (video->streaming->intf->num_altsetting == 1) - video->decode = uvc_video_encode_bulk; + if (stream->intf->num_altsetting == 1) + stream->decode = uvc_video_encode_bulk; else { uvc_printk(KERN_INFO, "Isochronous endpoints are not " "supported for video output devices.\n"); @@ -1133,31 +1153,32 @@ int uvc_video_init(struct uvc_video_device *video) /* * Enable or disable the video stream. */ -int uvc_video_enable(struct uvc_video_device *video, int enable) +int uvc_video_enable(struct uvc_streaming *stream, int enable) { int ret; if (!enable) { - uvc_uninit_video(video, 1); - usb_set_interface(video->dev->udev, - video->streaming->intfnum, 0); - uvc_queue_enable(&video->queue, 0); + uvc_uninit_video(stream, 1); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + uvc_queue_enable(&stream->queue, 0); return 0; } - if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || uvc_no_drop_param) - video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else - video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) return ret; /* Commit the streaming parameters. */ - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) return ret; - return uvc_init_video(video, GFP_KERNEL); + return uvc_init_video(stream, GFP_KERNEL); } diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 3c78d3c1e4c..e7958aa454c 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -67,155 +67,12 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include <linux/poll.h> +#include <linux/usb/video.h> /* -------------------------------------------------------------------------- * UVC constants */ -#define SC_UNDEFINED 0x00 -#define SC_VIDEOCONTROL 0x01 -#define SC_VIDEOSTREAMING 0x02 -#define SC_VIDEO_INTERFACE_COLLECTION 0x03 - -#define PC_PROTOCOL_UNDEFINED 0x00 - -#define CS_UNDEFINED 0x20 -#define CS_DEVICE 0x21 -#define CS_CONFIGURATION 0x22 -#define CS_STRING 0x23 -#define CS_INTERFACE 0x24 -#define CS_ENDPOINT 0x25 - -/* VideoControl class specific interface descriptor */ -#define VC_DESCRIPTOR_UNDEFINED 0x00 -#define VC_HEADER 0x01 -#define VC_INPUT_TERMINAL 0x02 -#define VC_OUTPUT_TERMINAL 0x03 -#define VC_SELECTOR_UNIT 0x04 -#define VC_PROCESSING_UNIT 0x05 -#define VC_EXTENSION_UNIT 0x06 - -/* VideoStreaming class specific interface descriptor */ -#define VS_UNDEFINED 0x00 -#define VS_INPUT_HEADER 0x01 -#define VS_OUTPUT_HEADER 0x02 -#define VS_STILL_IMAGE_FRAME 0x03 -#define VS_FORMAT_UNCOMPRESSED 0x04 -#define VS_FRAME_UNCOMPRESSED 0x05 -#define VS_FORMAT_MJPEG 0x06 -#define VS_FRAME_MJPEG 0x07 -#define VS_FORMAT_MPEG2TS 0x0a -#define VS_FORMAT_DV 0x0c -#define VS_COLORFORMAT 0x0d -#define VS_FORMAT_FRAME_BASED 0x10 -#define VS_FRAME_FRAME_BASED 0x11 -#define VS_FORMAT_STREAM_BASED 0x12 - -/* Endpoint type */ -#define EP_UNDEFINED 0x00 -#define EP_GENERAL 0x01 -#define EP_ENDPOINT 0x02 -#define EP_INTERRUPT 0x03 - -/* Request codes */ -#define RC_UNDEFINED 0x00 -#define SET_CUR 0x01 -#define GET_CUR 0x81 -#define GET_MIN 0x82 -#define GET_MAX 0x83 -#define GET_RES 0x84 -#define GET_LEN 0x85 -#define GET_INFO 0x86 -#define GET_DEF 0x87 - -/* VideoControl interface controls */ -#define VC_CONTROL_UNDEFINED 0x00 -#define VC_VIDEO_POWER_MODE_CONTROL 0x01 -#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 - -/* Terminal controls */ -#define TE_CONTROL_UNDEFINED 0x00 - -/* Selector Unit controls */ -#define SU_CONTROL_UNDEFINED 0x00 -#define SU_INPUT_SELECT_CONTROL 0x01 - -/* Camera Terminal controls */ -#define CT_CONTROL_UNDEFINED 0x00 -#define CT_SCANNING_MODE_CONTROL 0x01 -#define CT_AE_MODE_CONTROL 0x02 -#define CT_AE_PRIORITY_CONTROL 0x03 -#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 -#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 -#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 -#define CT_FOCUS_RELATIVE_CONTROL 0x07 -#define CT_FOCUS_AUTO_CONTROL 0x08 -#define CT_IRIS_ABSOLUTE_CONTROL 0x09 -#define CT_IRIS_RELATIVE_CONTROL 0x0a -#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b -#define CT_ZOOM_RELATIVE_CONTROL 0x0c -#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d -#define CT_PANTILT_RELATIVE_CONTROL 0x0e -#define CT_ROLL_ABSOLUTE_CONTROL 0x0f -#define CT_ROLL_RELATIVE_CONTROL 0x10 -#define CT_PRIVACY_CONTROL 0x11 - -/* Processing Unit controls */ -#define PU_CONTROL_UNDEFINED 0x00 -#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 -#define PU_BRIGHTNESS_CONTROL 0x02 -#define PU_CONTRAST_CONTROL 0x03 -#define PU_GAIN_CONTROL 0x04 -#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 -#define PU_HUE_CONTROL 0x06 -#define PU_SATURATION_CONTROL 0x07 -#define PU_SHARPNESS_CONTROL 0x08 -#define PU_GAMMA_CONTROL 0x09 -#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a -#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b -#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c -#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d -#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e -#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f -#define PU_HUE_AUTO_CONTROL 0x10 -#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 -#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 - -#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 -#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 -#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03 - -/* VideoStreaming interface controls */ -#define VS_CONTROL_UNDEFINED 0x00 -#define VS_PROBE_CONTROL 0x01 -#define VS_COMMIT_CONTROL 0x02 -#define VS_STILL_PROBE_CONTROL 0x03 -#define VS_STILL_COMMIT_CONTROL 0x04 -#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 -#define VS_STREAM_ERROR_CODE_CONTROL 0x06 -#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 -#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 -#define VS_SYNC_DELAY_CONTROL 0x09 - -#define TT_VENDOR_SPECIFIC 0x0100 -#define TT_STREAMING 0x0101 - -/* Input Terminal types */ -#define ITT_VENDOR_SPECIFIC 0x0200 -#define ITT_CAMERA 0x0201 -#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 - -/* Output Terminal types */ -#define OTT_VENDOR_SPECIFIC 0x0300 -#define OTT_DISPLAY 0x0301 -#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 - -/* External Terminal types */ -#define EXTERNAL_VENDOR_SPECIFIC 0x0400 -#define COMPOSITE_CONNECTOR 0x0401 -#define SVIDEO_CONNECTOR 0x0402 -#define COMPONENT_CONNECTOR 0x0403 - #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 @@ -223,12 +80,12 @@ struct uvc_xu_control { #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) #define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) #define UVC_ENTITY_IS_ITERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_INPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_INPUT) #define UVC_ENTITY_IS_OTERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) -#define UVC_STATUS_TYPE_CONTROL 1 -#define UVC_STATUS_TYPE_STREAMING 2 /* ------------------------------------------------------------------------ * GUIDs @@ -249,19 +106,6 @@ struct uvc_xu_control { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} -#define UVC_GUID_LOGITECH_DEV_INFO \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} -#define UVC_GUID_LOGITECH_USER_HW \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} -#define UVC_GUID_LOGITECH_VIDEO \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50} -#define UVC_GUID_LOGITECH_MOTOR \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} - #define UVC_GUID_FORMAT_MJPEG \ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} @@ -314,6 +158,7 @@ struct uvc_xu_control { #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 +#define UVC_QUIRK_PROBE_DEF 0x00000100 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -518,26 +363,6 @@ struct uvc_streaming_header { __u8 bTriggerUsage; }; -struct uvc_streaming { - struct list_head list; - - struct usb_interface *intf; - int intfnum; - __u16 maxpsize; - - struct uvc_streaming_header header; - enum v4l2_buf_type type; - - unsigned int nformats; - struct uvc_format *format; - - struct uvc_streaming_control ctrl; - struct uvc_format *cur_format; - struct uvc_frame *cur_frame; - - struct mutex mutex; -}; - enum uvc_buffer_state { UVC_BUF_STATE_IDLE = 0, UVC_BUF_STATE_QUEUED = 1, @@ -579,26 +404,45 @@ struct uvc_video_queue { struct list_head irqqueue; }; -struct uvc_video_device { +struct uvc_video_chain { struct uvc_device *dev; - struct video_device *vdev; - atomic_t active; - unsigned int frozen : 1; + struct list_head list; struct list_head iterms; /* Input terminals */ - struct uvc_entity *oterm; /* Output terminal */ - struct uvc_entity *sterm; /* USB streaming terminal */ - struct uvc_entity *processing; - struct uvc_entity *selector; - struct list_head extensions; + struct list_head oterms; /* Output terminals */ + struct uvc_entity *processing; /* Processing unit */ + struct uvc_entity *selector; /* Selector unit */ + struct list_head extensions; /* Extension units */ + struct mutex ctrl_mutex; +}; - struct uvc_video_queue queue; +struct uvc_streaming { + struct list_head list; + struct uvc_device *dev; + struct video_device *vdev; + struct uvc_video_chain *chain; + atomic_t active; - /* Video streaming object, must always be non-NULL. */ - struct uvc_streaming *streaming; + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; - void (*decode) (struct urb *urb, struct uvc_video_device *video, + struct uvc_streaming_header header; + enum v4l2_buf_type type; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; + + unsigned int frozen : 1; + struct uvc_video_queue queue; + void (*decode) (struct urb *urb, struct uvc_streaming *video, struct uvc_buffer *buf); /* Context data used by the bulk completion handler. */ @@ -640,8 +484,10 @@ struct uvc_device { __u32 clock_frequency; struct list_head entities; + struct list_head chains; - struct uvc_video_device video; + /* Video Streaming interfaces */ + struct list_head streams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -649,9 +495,6 @@ struct uvc_device { __u8 *status; struct input_dev *input; char input_phys[64]; - - /* Video Streaming interfaces */ - struct list_head streaming; }; enum uvc_handle_state { @@ -660,7 +503,8 @@ enum uvc_handle_state { }; struct uvc_fh { - struct uvc_video_device *device; + struct uvc_video_chain *chain; + struct uvc_streaming *stream; enum uvc_handle_state state; }; @@ -757,13 +601,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) extern const struct v4l2_file_operations uvc_fops; /* Video */ -extern int uvc_video_init(struct uvc_video_device *video); -extern int uvc_video_suspend(struct uvc_video_device *video); -extern int uvc_video_resume(struct uvc_video_device *video); -extern int uvc_video_enable(struct uvc_video_device *video, int enable); -extern int uvc_probe_video(struct uvc_video_device *video, +extern int uvc_video_init(struct uvc_streaming *stream); +extern int uvc_video_suspend(struct uvc_streaming *stream); +extern int uvc_video_resume(struct uvc_streaming *stream); +extern int uvc_video_enable(struct uvc_streaming *stream, int enable); +extern int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe); -extern int uvc_commit_video(struct uvc_video_device *video, +extern int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); @@ -777,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ -extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, +extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping); -extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_ctrl_add_info(struct uvc_control_info *info); @@ -789,23 +633,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); -extern int uvc_ctrl_begin(struct uvc_video_device *video); -extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_device *video) +extern int uvc_ctrl_begin(struct uvc_video_chain *chain); +extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 0); + return __uvc_ctrl_commit(chain, 0); } -static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 1); + return __uvc_ctrl_commit(chain, 1); } -extern int uvc_ctrl_get(struct uvc_video_device *video, +extern int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_ctrl_set(struct uvc_video_device *video, +extern int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_xu_ctrl_query(struct uvc_video_device *video, +extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *ctrl, int set); /* Utility functions */ @@ -817,7 +661,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint( struct usb_host_interface *alts, __u8 epaddr); /* Quirks support */ -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf); #endif /* __KERNEL__ */ diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 02f2a6d18b4..0c2105ca611 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -76,9 +76,8 @@ get_v4l_control(struct file *file, dprintk("VIDIOC_G_CTRL: %d\n", err); return 0; } - return ((ctrl2.value - qctrl2.minimum) * 65535 - + (qctrl2.maximum - qctrl2.minimum) / 2) - / (qctrl2.maximum - qctrl2.minimum); + return DIV_ROUND_CLOSEST((ctrl2.value-qctrl2.minimum) * 65535, + qctrl2.maximum - qctrl2.minimum); } return 0; } @@ -565,10 +564,9 @@ static noinline long v4l1_compat_get_input_info( break; } chan->norm = 0; - err = drv(file, VIDIOC_G_STD, &sid); - if (err < 0) - dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %ld\n", err); - if (err == 0) { + /* Note: G_STD might not be present for radio receivers, + * so we should ignore any errors. */ + if (drv(file, VIDIOC_G_STD, &sid) == 0) { if (sid & V4L2_STD_PAL) chan->norm = VIDEO_MODE_PAL; if (sid & V4L2_STD_NTSC) @@ -777,10 +775,9 @@ static noinline long v4l1_compat_get_tuner( tun->flags |= VIDEO_TUNER_SECAM; } - err = drv(file, VIDIOC_G_STD, &sid); - if (err < 0) - dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %ld\n", err); - if (err == 0) { + /* Note: G_STD might not be present for radio receivers, + * so we should ignore any errors. */ + if (drv(file, VIDIOC_G_STD, &sid) == 0) { if (sid & V4L2_STD_PAL) tun->mode = VIDEO_MODE_PAL; if (sid & V4L2_STD_NTSC) diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b91d66a767d..f5a93ae3cdf 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -156,6 +156,8 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, return -EINVAL; if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED) return -EBUSY; + if (qctrl->type == V4L2_CTRL_TYPE_STRING) + return 0; if (qctrl->type == V4L2_CTRL_TYPE_BUTTON || qctrl->type == V4L2_CTRL_TYPE_INTEGER64 || qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) @@ -340,6 +342,12 @@ const char **v4l2_ctrl_get_menu(u32 id) "Sepia", NULL }; + static const char *tune_preemphasis[] = { + "No preemphasis", + "50 useconds", + "75 useconds", + NULL, + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -378,6 +386,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return camera_exposure_auto; case V4L2_CID_COLORFX: return colorfx; + case V4L2_CID_TUNE_PREEMPHASIS: + return tune_preemphasis; default: return NULL; } @@ -476,6 +486,28 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; case V4L2_CID_PRIVACY: return "Privacy"; + /* FM Radio Modulator control */ + case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; + case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; + case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; + case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled"; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time"; + case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; + case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; + case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; + case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings"; + case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; + default: return NULL; } @@ -508,6 +540,9 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_EXPOSURE_AUTO_PRIORITY: case V4L2_CID_FOCUS_AUTO: case V4L2_CID_PRIVACY: + case V4L2_CID_AUDIO_LIMITER_ENABLED: + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + case V4L2_CID_PILOT_TONE_ENABLED: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; @@ -536,12 +571,18 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: case V4L2_CID_COLORFX: + case V4L2_CID_TUNE_PREEMPHASIS: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + qctrl->type = V4L2_CTRL_TYPE_STRING; + break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: + case V4L2_CID_FM_TX_CLASS: qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS; qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; @@ -570,6 +611,17 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_BLUE_BALANCE: case V4L2_CID_GAMMA: case V4L2_CID_SHARPNESS: + case V4L2_CID_RDS_TX_DEVIATION: + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + case V4L2_CID_PILOT_TONE_DEVIATION: + case V4L2_CID_PILOT_TONE_FREQUENCY: + case V4L2_CID_TUNE_POWER_LEVEL: + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: @@ -762,139 +814,6 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); /* Load an i2c sub-device. */ -struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, - const char *module_name, const char *client_type, u8 addr) -{ - struct v4l2_subdev *sd = NULL; - struct i2c_client *client; - struct i2c_board_info info; - - BUG_ON(!v4l2_dev); - - if (module_name) - request_module(module_name); - - /* Setup the i2c board info with the device type and - the device address. */ - memset(&info, 0, sizeof(info)); - strlcpy(info.type, client_type, sizeof(info.type)); - info.addr = addr; - - /* Create the i2c client */ - client = i2c_new_device(adapter, &info); - /* Note: it is possible in the future that - c->driver is NULL if the driver is still being loaded. - We need better support from the kernel so that we - can easily wait for the load to finish. */ - if (client == NULL || client->driver == NULL) - goto error; - - /* Lock the module so we can safely get the v4l2_subdev pointer */ - if (!try_module_get(client->driver->driver.owner)) - goto error; - sd = i2c_get_clientdata(client); - - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; - /* Decrease the module use count to match the first try_module_get. */ - module_put(client->driver->driver.owner); - - if (sd) { - /* We return errors from v4l2_subdev_call only if we have the - callback as the .s_config is not mandatory */ - int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); - - if (err && err != -ENOIOCTLCMD) { - v4l2_device_unregister_subdev(sd); - sd = NULL; - } - } - -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (client && sd == NULL) - i2c_unregister_device(client); - return sd; -} -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); - -/* Probe and load an i2c sub-device. */ -struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, - const char *module_name, const char *client_type, - const unsigned short *addrs) -{ - struct v4l2_subdev *sd = NULL; - struct i2c_client *client = NULL; - struct i2c_board_info info; - - BUG_ON(!v4l2_dev); - - if (module_name) - request_module(module_name); - - /* Setup the i2c board info with the device type and - the device address. */ - memset(&info, 0, sizeof(info)); - strlcpy(info.type, client_type, sizeof(info.type)); - - /* Probe and create the i2c client */ - client = i2c_new_probed_device(adapter, &info, addrs); - /* Note: it is possible in the future that - c->driver is NULL if the driver is still being loaded. - We need better support from the kernel so that we - can easily wait for the load to finish. */ - if (client == NULL || client->driver == NULL) - goto error; - - /* Lock the module so we can safely get the v4l2_subdev pointer */ - if (!try_module_get(client->driver->driver.owner)) - goto error; - sd = i2c_get_clientdata(client); - - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; - /* Decrease the module use count to match the first try_module_get. */ - module_put(client->driver->driver.owner); - - if (sd) { - /* We return errors from v4l2_subdev_call only if we have the - callback as the .s_config is not mandatory */ - int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); - - if (err && err != -ENOIOCTLCMD) { - v4l2_device_unregister_subdev(sd); - sd = NULL; - } - } - -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (client && sd == NULL) - i2c_unregister_device(client); - return sd; -} -EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); - -struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, - const char *module_name, const char *client_type, u8 addr) -{ - unsigned short addrs[2] = { addr, I2C_CLIENT_END }; - - return v4l2_i2c_new_probed_subdev(v4l2_dev, adapter, - module_name, client_type, addrs); -} -EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr); - -/* Load an i2c sub-device. */ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *module_name, struct i2c_board_info *info, const unsigned short *probe_addrs) diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 0056b115b42..997975d5e02 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -600,9 +600,37 @@ struct v4l2_ext_controls32 { compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; +struct v4l2_ext_control32 { + __u32 id; + __u32 size; + __u32 reserved2[1]; + union { + __s32 value; + __s64 value64; + compat_caddr_t string; /* actually char * */ + }; +} __attribute__ ((packed)); + +/* The following function really belong in v4l2-common, but that causes + a circular dependency between modules. We need to think about this, but + for now this will do. */ + +/* Return non-zero if this control is a pointer type. Currently only + type STRING is a pointer type. */ +static inline int ctrl_is_pointer(u32 id) +{ + switch (id) { + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + return 1; + default: + return 0; + } +} + static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols; int n; compat_caddr_t p; @@ -626,15 +654,17 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); kp->controls = kcontrols; while (--n >= 0) { - if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols))) return -EFAULT; + if (ctrl_is_pointer(kcontrols->id)) { + void __user *s; + + if (get_user(p, &ucontrols->string)) + return -EFAULT; + s = compat_ptr(p); + if (put_user(s, &kcontrols->string)) + return -EFAULT; + } ucontrols++; kcontrols++; } @@ -643,7 +673,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols = kp->controls; int n = kp->count; compat_caddr_t p; @@ -664,15 +694,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext return -EFAULT; while (--n >= 0) { - if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, - sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + unsigned size = sizeof(*ucontrols); + + /* Do not modify the pointer when copying a pointer control. + The contents of the pointer was changed, not the pointer + itself. */ + if (ctrl_is_pointer(kcontrols->id)) + size -= sizeof(ucontrols->value64); + if (copy_in_user(ucontrols, kcontrols, size)) return -EFAULT; ucontrols++; kcontrols++; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index a7f1b69a7da..500cbe9891a 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -66,7 +66,49 @@ static struct device_attribute video_device_attrs[] = { */ static struct video_device *video_device[VIDEO_NUM_DEVICES]; static DEFINE_MUTEX(videodev_lock); -static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); +static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); + +/* Device node utility functions */ + +/* Note: these utility functions all assume that vfl_type is in the range + [0, VFL_TYPE_MAX-1]. */ + +#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + /* Any types not assigned to fixed minor ranges must be mapped to + one single bitmap for the purposes of finding a free node number + since all those unassigned types use the same minor range. */ + int idx = (vfl_type > VFL_TYPE_VTX) ? VFL_TYPE_MAX - 1 : vfl_type; + + return devnode_nums[idx]; +} +#else +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + return devnode_nums[vfl_type]; +} +#endif + +/* Mark device node number vdev->num as used */ +static inline void devnode_set(struct video_device *vdev) +{ + set_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Mark device node number vdev->num as unused */ +static inline void devnode_clear(struct video_device *vdev) +{ + clear_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Try to find a free device node number in the range [from, to> */ +static inline int devnode_find(struct video_device *vdev, int from, int to) +{ + return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from); +} struct video_device *video_device_alloc(void) { @@ -119,8 +161,8 @@ static void v4l2_device_release(struct device *cd) the release() callback. */ vdev->cdev = NULL; - /* Mark minor as free */ - clear_bit(vdev->num, video_nums[vdev->vfl_type]); + /* Mark device node number as free */ + devnode_clear(vdev); mutex_unlock(&videodev_lock); @@ -299,32 +341,28 @@ static const struct file_operations v4l2_fops = { }; /** - * get_index - assign stream number based on parent device + * get_index - assign stream index number based on parent device * @vdev: video_device to assign index number to, vdev->parent should be assigned - * @num: -1 if auto assign, requested number otherwise * * Note that when this is called the new device has not yet been registered - * in the video_device array. + * in the video_device array, but it was able to obtain a minor number. + * + * This means that we can always obtain a free stream index number since + * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in + * use of the video_device array. * - * Returns -ENFILE if num is already in use, a free index number if - * successful. + * Returns a free index number. */ -static int get_index(struct video_device *vdev, int num) +static int get_index(struct video_device *vdev) { /* This can be static since this function is called with the global videodev_lock held. */ static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES); int i; - if (num >= VIDEO_NUM_DEVICES) { - printk(KERN_ERR "videodev: %s num is too large\n", __func__); - return -EINVAL; - } - - /* Some drivers do not set the parent. In that case always return - num or 0. */ + /* Some drivers do not set the parent. In that case always return 0. */ if (vdev->parent == NULL) - return num >= 0 ? num : 0; + return 0; bitmap_zero(used, VIDEO_NUM_DEVICES); @@ -335,35 +373,23 @@ static int get_index(struct video_device *vdev, int num) } } - if (num >= 0) { - if (test_bit(num, used)) - return -ENFILE; - return num; - } - - i = find_first_zero_bit(used, VIDEO_NUM_DEVICES); - return i == VIDEO_NUM_DEVICES ? -ENFILE : i; + return find_first_zero_bit(used, VIDEO_NUM_DEVICES); } -int video_register_device(struct video_device *vdev, int type, int nr) -{ - return video_register_device_index(vdev, type, nr, -1); -} -EXPORT_SYMBOL(video_register_device); - /** - * video_register_device_index - register video4linux devices + * video_register_device - register video4linux devices * @vdev: video device structure we want to register * @type: type of device to register - * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... + * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) - * @index: stream number based on parent device; - * -1 if auto assign, requested number otherwise + * @warn_if_nr_in_use: warn if the desired device node number + * was already in use and another number was chosen instead. * - * The registration code assigns minor numbers based on the type - * requested. -ENFILE is returned in all the device slots for this - * category are full. If not then the minor field is set and the - * driver initialize function is called (if non %NULL). + * The registration code assigns minor numbers and device node numbers + * based on the requested type and registers the new device node with + * the kernel. + * An error is returned if no free minor or device node number could be + * found, or if the registration of the device node failed. * * Zero is returned on success. * @@ -377,8 +403,8 @@ EXPORT_SYMBOL(video_register_device); * * %VFL_TYPE_RADIO - A radio card */ -int video_register_device_index(struct video_device *vdev, int type, int nr, - int index) +static int __video_register_device(struct video_device *vdev, int type, int nr, + int warn_if_nr_in_use) { int i = 0; int ret; @@ -421,7 +447,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; - /* Part 2: find a free minor, kernel number and device index. */ + /* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -452,21 +478,22 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, } #endif - /* Pick a minor number */ + /* Pick a device node number */ mutex_lock(&videodev_lock); - nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); + nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); if (nr == minor_cnt) - nr = find_first_zero_bit(video_nums[type], minor_cnt); + nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) { - printk(KERN_ERR "could not get a free kernel number\n"); + printk(KERN_ERR "could not get a free device node number\n"); mutex_unlock(&videodev_lock); return -ENFILE; } #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES - /* 1-on-1 mapping of kernel number to minor number */ + /* 1-on-1 mapping of device node number to minor number */ i = nr; #else - /* The kernel number and minor numbers are independent */ + /* The device node number and minor numbers are independent, so + we just find the first free minor number. */ for (i = 0; i < VIDEO_NUM_DEVICES; i++) if (video_device[i] == NULL) break; @@ -478,17 +505,13 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, #endif vdev->minor = i + minor_offset; vdev->num = nr; - set_bit(nr, video_nums[type]); + devnode_set(vdev); + /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); - ret = vdev->index = get_index(vdev, index); + vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); - if (ret < 0) { - printk(KERN_ERR "%s: get_index failed\n", __func__); - goto cleanup; - } - /* Part 3: Initialize the character device */ vdev->cdev = cdev_alloc(); if (vdev->cdev == NULL) { @@ -517,7 +540,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); if (vdev->parent) vdev->dev.parent = vdev->parent; - dev_set_name(&vdev->dev, "%s%d", name_base, nr); + dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); @@ -527,6 +550,10 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, reference to the device goes away. */ vdev->dev.release = v4l2_device_release; + if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) + printk(KERN_WARNING "%s: requested %s%d, got %s%d\n", + __func__, name_base, nr, name_base, vdev->num); + /* Part 5: Activate this minor. The char device can now be used. */ mutex_lock(&videodev_lock); video_device[vdev->minor] = vdev; @@ -537,13 +564,24 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); - clear_bit(vdev->num, video_nums[type]); + devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ vdev->minor = -1; return ret; } -EXPORT_SYMBOL(video_register_device_index); + +int video_register_device(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 1); +} +EXPORT_SYMBOL(video_register_device); + +int video_register_device_no_warn(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 0); +} +EXPORT_SYMBOL(video_register_device_no_warn); /** * video_unregister_device - unregister a video4linux device diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index be64a502ea2..30cc3347ae5 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -42,6 +42,12 @@ printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ } while (0) +#define dbgarg3(fmt, arg...) \ + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ + printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\ + } while (0) + /* Zero out the end of the struct pointed to by p. Everthing after, but * not including, the specified field is cleared. */ #define CLEAR_AFTER_FIELD(p, field) \ @@ -507,11 +513,12 @@ static inline void v4l_print_ext_ctrls(unsigned int cmd, dbgarg(cmd, ""); printk(KERN_CONT "class=0x%x", c->ctrl_class); for (i = 0; i < c->count; i++) { - if (show_vals) + if (show_vals && !c->controls[i].size) printk(KERN_CONT " id/val=0x%x/0x%x", c->controls[i].id, c->controls[i].value); else - printk(KERN_CONT " id=0x%x", c->controls[i].id); + printk(KERN_CONT " id=0x%x,size=%u", + c->controls[i].id, c->controls[i].size); } printk(KERN_CONT "\n"); }; @@ -522,10 +529,9 @@ static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) /* zero the reserved fields */ c->reserved[0] = c->reserved[1] = 0; - for (i = 0; i < c->count; i++) { + for (i = 0; i < c->count; i++) c->controls[i].reserved2[0] = 0; - c->controls[i].reserved2[1] = 0; - } + /* V4L2_CID_PRIVATE_BASE cannot be used as control class when using extended controls. Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL @@ -1081,8 +1087,10 @@ static long __video_do_ioctl(struct file *file, /* Calls the specific handler */ if (ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, id); - else + else if (vfd->current_norm) *id = vfd->current_norm; + else + ret = -EINVAL; if (!ret) dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); @@ -1553,12 +1561,19 @@ static long __video_do_ioctl(struct file *file, break; ret = ops->vidioc_g_parm(file, fh, p); } else { + v4l2_std_id std = vfd->current_norm; + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - v4l2_video_std_frame_period(vfd->current_norm, - &p->parm.capture.timeperframe); ret = 0; + if (ops->vidioc_g_std) + ret = ops->vidioc_g_std(file, fh, &std); + else if (std == 0) + ret = -EINVAL; + if (ret == 0) + v4l2_video_std_frame_period(std, + &p->parm.capture.timeperframe); } dbgarg(cmd, "type=%d\n", p->type); @@ -1717,24 +1732,29 @@ static long __video_do_ioctl(struct file *file, ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, - "index=%d, pixelformat=%d, type=%d ", - p->index, p->pixel_format, p->type); + "index=%d, pixelformat=%c%c%c%c, type=%d ", + p->index, + (p->pixel_format & 0xff), + (p->pixel_format >> 8) & 0xff, + (p->pixel_format >> 16) & 0xff, + (p->pixel_format >> 24) & 0xff, + p->type); switch (p->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: - dbgarg2("width = %d, height=%d\n", + dbgarg3("width = %d, height=%d\n", p->discrete.width, p->discrete.height); break; case V4L2_FRMSIZE_TYPE_STEPWISE: - dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + dbgarg3("min %dx%d, max %dx%d, step %dx%d\n", p->stepwise.min_width, p->stepwise.min_height, p->stepwise.step_width, p->stepwise.step_height, p->stepwise.max_width, p->stepwise.max_height); break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: - dbgarg2("continuous\n"); + dbgarg3("continuous\n"); break; default: - dbgarg2("- Unknown type!\n"); + dbgarg3("- Unknown type!\n"); } break; diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 97b082fe447..cd6a3446ab7 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -1776,7 +1776,6 @@ static struct i2c_algo_sgi_data i2c_sgi_vino_data = { static struct i2c_adapter vino_i2c_adapter = { .name = "VINO I2C bus", - .id = I2C_HW_SGI_VINO, .algo = &sgi_algo, .algo_data = &i2c_sgi_vino_data, .owner = THIS_MODULE, @@ -4334,11 +4333,11 @@ static int __init vino_module_init(void) vino_init_stage++; vino_drvdata->decoder = - v4l2_i2c_new_probed_subdev_addr(&vino_drvdata->v4l2_dev, - &vino_i2c_adapter, "saa7191", "saa7191", 0x45); + v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter, + "saa7191", "saa7191", 0, I2C_ADDRS(0x45)); vino_drvdata->camera = - v4l2_i2c_new_probed_subdev_addr(&vino_drvdata->v4l2_dev, - &vino_i2c_adapter, "indycam", "indycam", 0x2b); + v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter, + "indycam", "indycam", 0, I2C_ADDRS(0x2b)); dprintk("init complete!\n"); diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 6c3f23e31b5..37fcdc447db 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -1497,7 +1497,6 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) }; static struct i2c_adapter adap = { - .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, .algo = &algo, }; @@ -3516,9 +3515,9 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_turn_on_led(cam); w9968cf_i2c_init(cam); - cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->v4l2_dev, + cam->sensor_sd = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter, - "ovcamchip", "ovcamchip", addrs); + "ovcamchip", "ovcamchip", 0, addrs); usb_set_intfdata(intf, cam); mutex_unlock(&cam->dev_mutex); diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 96971044fc7..b3c6436b33b 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -819,8 +819,10 @@ zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + msecs_to_jiffies( + cam->module_param.frame_timeout * 1000 + ) + ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index 03dc2f3cf84..be70574870d 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -732,7 +732,6 @@ zoran_register_i2c (struct zoran *zr) memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, sizeof(struct i2c_algo_bit_data)); zr->i2c_algo.data = zr; - zr->i2c_adapter.id = I2C_HW_B_ZR36067; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); @@ -1169,7 +1168,7 @@ zoran_setup_videocodec (struct zoran *zr, m->type = 0; m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); m->data = zr; switch (type) @@ -1358,15 +1357,15 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_free_irq; } - zr->decoder = v4l2_i2c_new_probed_subdev(&zr->v4l2_dev, + zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, zr->card.mod_decoder, zr->card.i2c_decoder, - zr->card.addrs_decoder); + 0, zr->card.addrs_decoder); if (zr->card.mod_encoder) - zr->encoder = v4l2_i2c_new_probed_subdev(&zr->v4l2_dev, + zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, zr->card.mod_encoder, zr->card.i2c_encoder, - zr->card.addrs_encoder); + 0, zr->card.addrs_encoder); dprintk(2, KERN_INFO "%s: Initializing videocodec bus...\n", diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index fc976f42f43..9aae011d92a 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -1,5 +1,5 @@ /* - * Zoran 364xx based USB webcam module version 0.72 + * Zoran 364xx based USB webcam module version 0.73 * * Allows you to use your USB webcam with V4L2 applications * This is still in heavy developpement ! @@ -10,6 +10,8 @@ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers * V4L2 version inspired by meye.c driver * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + * * 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 @@ -27,6 +29,7 @@ #include <linux/module.h> +#include <linux/version.h> #include <linux/init.h> #include <linux/usb.h> #include <linux/vmalloc.h> @@ -35,24 +38,40 @@ #include <linux/highmem.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/videobuf-vmalloc.h> /* Version Information */ -#define DRIVER_VERSION "v0.72" +#define DRIVER_VERSION "v0.73" +#define ZR364XX_VERSION_CODE KERNEL_VERSION(0, 7, 3) #define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" #define DRIVER_DESC "Zoran 364xx" /* Camera */ -#define FRAMES 2 -#define MAX_FRAME_SIZE 100000 +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 #define BUFFER_SIZE 0x1000 #define CTRL_TIMEOUT 500 +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 /* Debug macro */ -#define DBG(x...) if (debug) printk(KERN_INFO KBUILD_MODNAME x) - +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif /* Init methods, need to find nicer names for these * the exact names of the chipsets would be the best if someone finds it */ @@ -101,24 +120,93 @@ static struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); +struct zr364xx_mode { + u32 color; /* output video color format */ + u32 brightness; /* brightness */ +}; + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; /* Camera stuff */ struct zr364xx_camera { struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface;/* the interface for this device */ struct video_device *vdev; /* v4l video device */ - u8 *framebuf; int nb; - unsigned char *buffer; + struct zr364xx_bufferi buffer; int skip; - int brightness; int width; int height; int method; struct mutex lock; + struct mutex open_lock; int users; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int resources; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + struct zr364xx_mode mode; }; +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; /* function used to send initialisation commands to the camera */ static int send_control_msg(struct usb_device *udev, u8 request, u16 value, @@ -272,139 +360,116 @@ static unsigned char header2[] = { }; static unsigned char header3; +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; -/********************/ -/* V4L2 integration */ -/********************/ + *size = cam->width * cam->height * (cam->fmt->depth >> 3); -/* this function reads a full JPEG picture synchronously - * TODO: do it asynchronously... */ -static int read_frame(struct zr364xx_camera *cam, int framenum) -{ - int i, n, temp, head, size, actual_length; - unsigned char *ptr = NULL, *jpeg; - - redo: - /* hardware brightness */ - n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - cam->brightness; - n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - - /* during the first loop we are going to insert JPEG header */ - head = 0; - /* this is the place in memory where we are going to build - * the JPEG image */ - jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; - /* read data... */ - do { - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, 0x81), - cam->buffer, BUFFER_SIZE, &actual_length, - CTRL_TIMEOUT); - DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); - DBG("bulk : n=%d size=%d", n, actual_length); - if (n < 0) { - dev_err(&cam->udev->dev, "error reading bulk msg\n"); - return 0; - } - if (actual_length < 0 || actual_length > BUFFER_SIZE) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return 0; - } + if (*count == 0) + *count = ZR364XX_DEF_BUFS; - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16*)cam->buffer; - for (i = 0; i < BUFFER_SIZE/2; i++) - swab16s(buf + i); - } + while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024) + (*count)--; - /* write the JPEG header */ - if (!head) { - DBG("jpeg header"); - ptr = jpeg; - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, cam->buffer + 128, - actual_length - 128); - ptr += actual_length - 128; - head = 1; - DBG("header : %d %d %d %d %d %d %d %d %d", - cam->buffer[0], cam->buffer[1], cam->buffer[2], - cam->buffer[3], cam->buffer[4], cam->buffer[5], - cam->buffer[6], cam->buffer[7], cam->buffer[8]); - } else { - memcpy(ptr, cam->buffer, actual_length); - ptr += actual_length; - } - } - /* ... until there is no more */ - while (actual_length == BUFFER_SIZE); + return 0; +} - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) { - cam->skip--; - goto redo; - } +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); - /* go back to find the JPEG EOI marker */ - size = ptr - jpeg; - ptr -= 2; - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == jpeg) - DBG("No EOI marker"); + if (in_interrupt()) + BUG(); - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? + cam->fmt->name : ""); + if (cam->fmt == NULL) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; } - if (ptr != jpeg) { - DBG("Bogus frame ? %d", cam->nb); - goto redo; + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; } - DBG("jpeg : %d %d %d %d %d %d %d %d", - jpeg[0], jpeg[1], jpeg[2], jpeg[3], - jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} - return size; +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); } +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + _DBG("%s\n", __func__); + free_buffer(vq, buf); +} -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, +static struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); + +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { - unsigned long count = cnt; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); - DBG("zr364xx_read: read %d bytes.", (int) count); - - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + _DBG("%s\n", __func__); if (!buf) return -EINVAL; @@ -412,21 +477,276 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, if (!count) return -EINVAL; - /* NoMan Sux ! */ - count = read_frame(cam, 0); + if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + zr364xx_vidioc_streamon(file, cam, cam->type) == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count, + (int) *ppos); + + /* NoMan Sux ! */ + return videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } - if (copy_to_user(buf, cam->framebuf, count)) - return -EFAULT; + return 0; +} - return count; +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> + * Ted Walther <ted--a.t--enumera.com> + * John Sokol <sokol--a.t--videotechnology.com> + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + struct zr364xx_framei *frm; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + frm = &cam->buffer.frame[last_frame]; + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer 0x%08lx size= %d\n", __func__, + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; } +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return 0; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = -1; + struct zr364xx_framei *frm; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + idx = cam->cur_frame; + frm = &cam->buffer.frame[idx]; + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold " + "frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int res_get(struct zr364xx_camera *cam) +{ + /* is it free? */ + mutex_lock(&cam->lock); + if (cam->resources) { + /* no, someone else uses it */ + mutex_unlock(&cam->lock); + return 0; + } + /* it's free, grab it */ + cam->resources = 1; + _DBG("res: get\n"); + mutex_unlock(&cam->lock); + return 1; +} + +static inline int res_check(struct zr364xx_camera *cam) +{ + return cam->resources; +} + +static void res_free(struct zr364xx_camera *cam) +{ + mutex_lock(&cam->lock); + cam->resources = 0; + mutex_unlock(&cam->lock); + _DBG("res: put\n"); +} static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - strcpy(cap->driver, DRIVER_DESC); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + struct zr364xx_camera *cam = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + cap->version = ZR364XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; } @@ -458,12 +778,11 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv, static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -472,7 +791,7 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, c->minimum = 0; c->maximum = 127; c->step = 1; - c->default_value = cam->brightness; + c->default_value = cam->mode.brightness; c->flags = 0; break; default: @@ -484,36 +803,42 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; + int temp; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - cam->brightness = c->value; + cam->mode.brightness = c->value; + /* hardware brightness */ + mutex_lock(&cam->lock); + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->mode.brightness; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + mutex_unlock(&cam->lock); break; default: return -EINVAL; } + return 0; } static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - c->value = cam->brightness; + c->value = cam->mode.brightness; break; default: return -EINVAL; @@ -527,47 +852,63 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, if (f->index > 0) return -EINVAL; f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, "JPEG"); - f->pixelformat = V4L2_PIX_FMT_JPEG; + strcpy(f->description, formats[0].name); + f->pixelformat = formats[0].fourcc; return 0; } +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; - if (vdev == NULL) + if (cam == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; f->fmt.pix.priv = 0; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); return 0; } static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.pixelformat = formats[0].fourcc; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -581,38 +922,327 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + if (ret < 0) + return ret; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (res_check(cam)) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; f->fmt.pix.priv = 0; - DBG("ok!"); + cam->vb_vidq.field = f->fmt.pix.field; + cam->mode.color = V4L2_PIX_FMT_JPEG; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_reqbufs(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (pipe_info == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length < 0 || + purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; return 0; } static int zr364xx_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - return 0; + struct zr364xx_camera *cam = video_drvdata(file); + int j; + int res; + + DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(cam)) { + dev_err(&cam->udev->dev, "stream busy\n"); + return -EBUSY; + } + + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + } else { + res_free(cam); + } + return res; } static int zr364xx_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { + int res; + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + zr364xx_stop_acquire(cam); + res = videobuf_streamoff(&cam->vb_vidq); + if (res < 0) + return res; + res_free(cam); return 0; } @@ -621,28 +1251,19 @@ static int zr364xx_vidioc_streamoff(struct file *file, void *priv, static int zr364xx_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct zr364xx_camera *cam = video_drvdata(file); struct usb_device *udev = cam->udev; int i, err; - DBG("zr364xx_open"); + DBG("%s\n", __func__); - mutex_lock(&cam->lock); + mutex_lock(&cam->open_lock); if (cam->users) { err = -EBUSY; goto out; } - if (!cam->framebuf) { - cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); - if (!cam->framebuf) { - dev_err(&cam->udev->dev, "vmalloc_32 failed!\n"); - err = -ENOMEM; - goto out; - } - } - for (i = 0; init[cam->method][i].size != -1; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -658,6 +1279,14 @@ static int zr364xx_open(struct file *file) cam->skip = 2; cam->users++; file->private_data = vdev; + cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + cam->type, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam); /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam @@ -666,28 +1295,70 @@ static int zr364xx_open(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + DBG("%s: %d\n", __func__, err); return err; } +static void zr364xx_destroy(struct zr364xx_camera *cam) +{ + unsigned long i; + + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__); + return; + } + mutex_lock(&cam->open_lock); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + mutex_unlock(&cam->open_lock); + kfree(cam); + cam = NULL; +} /* release the camera */ static int zr364xx_release(struct file *file) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; struct usb_device *udev; int i, err; - DBG("zr364xx_release"); + DBG("%s\n", __func__); + cam = video_drvdata(file); - if (vdev == NULL) + if (!cam) return -ENODEV; - cam = video_get_drvdata(vdev); + mutex_lock(&cam->open_lock); udev = cam->udev; - mutex_lock(&cam->lock); + /* turn off stream */ + if (res_check(cam)) { + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + res_free(cam); + } cam->users--; file->private_data = NULL; @@ -695,7 +1366,7 @@ static int zr364xx_release(struct file *file) for (i = 0; i < 2; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[i][cam->method].bytes, + 0, init[cam->method][i].bytes, init[cam->method][i].size); if (err < 0) { dev_err(&udev->dev, "error during release sequence\n"); @@ -710,40 +1381,43 @@ static int zr364xx_release(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + return err; } static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) { - void *pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; - - DBG("zr364xx_mmap: %ld\n", size); + struct zr364xx_camera *cam = video_drvdata(file); + int ret; - if (vdev == NULL) + if (cam == NULL) { + DBG("%s: cam == NULL\n", __func__); return -ENODEV; - cam = video_get_drvdata(vdev); - - pos = cam->framebuf; - while (size > 0) { - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; } + DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); - return 0; + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; } +static unsigned int zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + _DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return POLLERR; + + return videobuf_poll_stream(file, q, wait); +} static const struct v4l2_file_operations zr364xx_fops = { .owner = THIS_MODULE, @@ -752,6 +1426,7 @@ static const struct v4l2_file_operations zr364xx_fops = { .read = zr364xx_read, .mmap = zr364xx_mmap, .ioctl = video_ioctl2, + .poll = zr364xx_poll, }; static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { @@ -768,6 +1443,10 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { .vidioc_queryctrl = zr364xx_vidioc_queryctrl, .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, }; static struct video_device zr364xx_template = { @@ -783,15 +1462,76 @@ static struct video_device zr364xx_template = { /*******************/ /* USB integration */ /*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (cam->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. " + "Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return -ENOMEM; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + zr364xx_start_readpipe(cam); + DBG(": board initialized\n"); + return 0; +} static int zr364xx_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; int err; + int i; - DBG("probing..."); + DBG("probing...\n"); dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); dev_info(&intf->dev, "model %04x:%04x detected\n", @@ -810,22 +1550,17 @@ static int zr364xx_probe(struct usb_interface *intf, if (cam->vdev == NULL) { dev_err(&udev->dev, "cam->vdev: out of memory !\n"); kfree(cam); + cam = NULL; return -ENOMEM; } memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + cam->vdev->parent = &intf->dev; video_set_drvdata(cam->vdev, cam); if (debug) cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; - if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { - dev_info(&udev->dev, "cam->buffer: out of memory !\n"); - video_device_release(cam->vdev); - kfree(cam); - return -ENODEV; - } - switch (mode) { case 1: dev_info(&udev->dev, "160x120 mode selected\n"); @@ -852,21 +1587,53 @@ static int zr364xx_probe(struct usb_interface *intf, header2[439] = cam->width / 256; header2[440] = cam->width % 256; + cam->users = 0; cam->nb = 0; - cam->brightness = 64; + cam->mode.brightness = 64; mutex_init(&cam->lock); + mutex_init(&cam->open_lock); + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + return -ENOMEM; + } + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); video_device_release(cam->vdev); - kfree(cam->buffer); kfree(cam); + cam = NULL; return err; } usb_set_intfdata(intf, cam); + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (err) { + spin_lock_init(&cam->slock); + return err; + } + + spin_lock_init(&cam->slock); + dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n", cam->vdev->num); return 0; @@ -876,17 +1643,10 @@ static int zr364xx_probe(struct usb_interface *intf, static void zr364xx_disconnect(struct usb_interface *intf) { struct zr364xx_camera *cam = usb_get_intfdata(intf); + videobuf_mmap_free(&cam->vb_vidq); usb_set_intfdata(intf, NULL); dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - if (cam->vdev) - video_unregister_device(cam->vdev); - cam->vdev = NULL; - kfree(cam->buffer); - cam->buffer = NULL; - vfree(cam->framebuf); - cam->framebuf = NULL; - kfree(cam); - cam = NULL; + zr364xx_destroy(cam); } |