diff options
Diffstat (limited to 'drivers/media')
26 files changed, 3694 insertions, 183 deletions
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 2d8b4044be36..b2b0c45f32a9 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/i2c.h> | 22 | #include <linux/i2c.h> |
23 | #include <linux/i2c-algo-bit.h> | ||
23 | #include <linux/init.h> | 24 | #include <linux/init.h> |
24 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
25 | #include <linux/module.h> | 26 | #include <linux/module.h> |
@@ -49,11 +50,12 @@ | |||
49 | 50 | ||
50 | #define UNSET (-1U) | 51 | #define UNSET (-1U) |
51 | 52 | ||
52 | #define DM1105_BOARD_NOAUTO UNSET | 53 | #define DM1105_BOARD_NOAUTO UNSET |
53 | #define DM1105_BOARD_UNKNOWN 0 | 54 | #define DM1105_BOARD_UNKNOWN 0 |
54 | #define DM1105_BOARD_DVBWORLD_2002 1 | 55 | #define DM1105_BOARD_DVBWORLD_2002 1 |
55 | #define DM1105_BOARD_DVBWORLD_2004 2 | 56 | #define DM1105_BOARD_DVBWORLD_2004 2 |
56 | #define DM1105_BOARD_AXESS_DM05 3 | 57 | #define DM1105_BOARD_AXESS_DM05 3 |
58 | #define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 | ||
57 | 59 | ||
58 | /* ----------------------------------------------- */ | 60 | /* ----------------------------------------------- */ |
59 | /* | 61 | /* |
@@ -157,22 +159,38 @@ | |||
157 | #define DM1105_MAX 0x04 | 159 | #define DM1105_MAX 0x04 |
158 | 160 | ||
159 | #define DRIVER_NAME "dm1105" | 161 | #define DRIVER_NAME "dm1105" |
162 | #define DM1105_I2C_GPIO_NAME "dm1105-gpio" | ||
160 | 163 | ||
161 | #define DM1105_DMA_PACKETS 47 | 164 | #define DM1105_DMA_PACKETS 47 |
162 | #define DM1105_DMA_PACKET_LENGTH (128*4) | 165 | #define DM1105_DMA_PACKET_LENGTH (128*4) |
163 | #define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) | 166 | #define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) |
164 | 167 | ||
168 | /* */ | ||
169 | #define GPIO08 (1 << 8) | ||
170 | #define GPIO13 (1 << 13) | ||
171 | #define GPIO14 (1 << 14) | ||
172 | #define GPIO15 (1 << 15) | ||
173 | #define GPIO16 (1 << 16) | ||
174 | #define GPIO17 (1 << 17) | ||
175 | #define GPIO_ALL 0x03ffff | ||
176 | |||
165 | /* GPIO's for LNB power control */ | 177 | /* GPIO's for LNB power control */ |
166 | #define DM1105_LNB_MASK 0x00000000 | 178 | #define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) |
167 | #define DM1105_LNB_OFF 0x00020000 | 179 | #define DM1105_LNB_OFF GPIO17 |
168 | #define DM1105_LNB_13V 0x00010100 | 180 | #define DM1105_LNB_13V (GPIO16 | GPIO08) |
169 | #define DM1105_LNB_18V 0x00000100 | 181 | #define DM1105_LNB_18V GPIO08 |
170 | 182 | ||
171 | /* GPIO's for LNB power control for Axess DM05 */ | 183 | /* GPIO's for LNB power control for Axess DM05 */ |
172 | #define DM05_LNB_MASK 0x00000000 | 184 | #define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) |
173 | #define DM05_LNB_OFF 0x00020000/* actually 13v */ | 185 | #define DM05_LNB_OFF GPIO17/* actually 13v */ |
174 | #define DM05_LNB_13V 0x00020000 | 186 | #define DM05_LNB_13V GPIO17 |
175 | #define DM05_LNB_18V 0x00030000 | 187 | #define DM05_LNB_18V (GPIO17 | GPIO16) |
188 | |||
189 | /* GPIO's for LNB power control for unbranded with I2C on GPIO */ | ||
190 | #define UNBR_LNB_MASK (GPIO17 | GPIO16) | ||
191 | #define UNBR_LNB_OFF 0 | ||
192 | #define UNBR_LNB_13V GPIO17 | ||
193 | #define UNBR_LNB_18V (GPIO17 | GPIO16) | ||
176 | 194 | ||
177 | static unsigned int card[] = {[0 ... 3] = UNSET }; | 195 | static unsigned int card[] = {[0 ... 3] = UNSET }; |
178 | module_param_array(card, int, NULL, 0444); | 196 | module_param_array(card, int, NULL, 0444); |
@@ -187,7 +205,11 @@ static unsigned int dm1105_devcount; | |||
187 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | 205 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
188 | 206 | ||
189 | struct dm1105_board { | 207 | struct dm1105_board { |
190 | char *name; | 208 | char *name; |
209 | struct { | ||
210 | u32 mask, off, v13, v18; | ||
211 | } lnb; | ||
212 | u32 gpio_scl, gpio_sda; | ||
191 | }; | 213 | }; |
192 | 214 | ||
193 | struct dm1105_subid { | 215 | struct dm1105_subid { |
@@ -199,15 +221,50 @@ struct dm1105_subid { | |||
199 | static const struct dm1105_board dm1105_boards[] = { | 221 | static const struct dm1105_board dm1105_boards[] = { |
200 | [DM1105_BOARD_UNKNOWN] = { | 222 | [DM1105_BOARD_UNKNOWN] = { |
201 | .name = "UNKNOWN/GENERIC", | 223 | .name = "UNKNOWN/GENERIC", |
224 | .lnb = { | ||
225 | .mask = DM1105_LNB_MASK, | ||
226 | .off = DM1105_LNB_OFF, | ||
227 | .v13 = DM1105_LNB_13V, | ||
228 | .v18 = DM1105_LNB_18V, | ||
229 | }, | ||
202 | }, | 230 | }, |
203 | [DM1105_BOARD_DVBWORLD_2002] = { | 231 | [DM1105_BOARD_DVBWORLD_2002] = { |
204 | .name = "DVBWorld PCI 2002", | 232 | .name = "DVBWorld PCI 2002", |
233 | .lnb = { | ||
234 | .mask = DM1105_LNB_MASK, | ||
235 | .off = DM1105_LNB_OFF, | ||
236 | .v13 = DM1105_LNB_13V, | ||
237 | .v18 = DM1105_LNB_18V, | ||
238 | }, | ||
205 | }, | 239 | }, |
206 | [DM1105_BOARD_DVBWORLD_2004] = { | 240 | [DM1105_BOARD_DVBWORLD_2004] = { |
207 | .name = "DVBWorld PCI 2004", | 241 | .name = "DVBWorld PCI 2004", |
242 | .lnb = { | ||
243 | .mask = DM1105_LNB_MASK, | ||
244 | .off = DM1105_LNB_OFF, | ||
245 | .v13 = DM1105_LNB_13V, | ||
246 | .v18 = DM1105_LNB_18V, | ||
247 | }, | ||
208 | }, | 248 | }, |
209 | [DM1105_BOARD_AXESS_DM05] = { | 249 | [DM1105_BOARD_AXESS_DM05] = { |
210 | .name = "Axess/EasyTv DM05", | 250 | .name = "Axess/EasyTv DM05", |
251 | .lnb = { | ||
252 | .mask = DM05_LNB_MASK, | ||
253 | .off = DM05_LNB_OFF, | ||
254 | .v13 = DM05_LNB_13V, | ||
255 | .v18 = DM05_LNB_18V, | ||
256 | }, | ||
257 | }, | ||
258 | [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { | ||
259 | .name = "Unbranded DM1105 with i2c on GPIOs", | ||
260 | .lnb = { | ||
261 | .mask = UNBR_LNB_MASK, | ||
262 | .off = UNBR_LNB_OFF, | ||
263 | .v13 = UNBR_LNB_13V, | ||
264 | .v18 = UNBR_LNB_18V, | ||
265 | }, | ||
266 | .gpio_scl = GPIO14, | ||
267 | .gpio_sda = GPIO13, | ||
211 | }, | 268 | }, |
212 | }; | 269 | }; |
213 | 270 | ||
@@ -293,6 +350,8 @@ struct dm1105_dev { | |||
293 | 350 | ||
294 | /* i2c */ | 351 | /* i2c */ |
295 | struct i2c_adapter i2c_adap; | 352 | struct i2c_adapter i2c_adap; |
353 | struct i2c_adapter i2c_bb_adap; | ||
354 | struct i2c_algo_bit_data i2c_bit; | ||
296 | 355 | ||
297 | /* irq */ | 356 | /* irq */ |
298 | struct work_struct work; | 357 | struct work_struct work; |
@@ -328,6 +387,103 @@ struct dm1105_dev { | |||
328 | #define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) | 387 | #define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) |
329 | #define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) | 388 | #define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) |
330 | 389 | ||
390 | /* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, | ||
391 | so we can use only 3 GPIO's from GPIO15 to GPIO17. | ||
392 | Here I don't check whether HOST is enebled as it is not implemented yet. | ||
393 | */ | ||
394 | static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) | ||
395 | { | ||
396 | if (mask & 0xfffc0000) | ||
397 | printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); | ||
398 | |||
399 | if (mask & 0x0003ffff) | ||
400 | dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); | ||
401 | |||
402 | } | ||
403 | |||
404 | static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) | ||
405 | { | ||
406 | if (mask & 0xfffc0000) | ||
407 | printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); | ||
408 | |||
409 | if (mask & 0x0003ffff) | ||
410 | dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); | ||
411 | |||
412 | } | ||
413 | |||
414 | static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) | ||
415 | { | ||
416 | if (mask & 0xfffc0000) | ||
417 | printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); | ||
418 | |||
419 | if (mask & 0x0003ffff) | ||
420 | dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); | ||
421 | |||
422 | } | ||
423 | |||
424 | static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) | ||
425 | { | ||
426 | if (mask & 0xfffc0000) | ||
427 | printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); | ||
428 | |||
429 | if (mask & 0x0003ffff) | ||
430 | return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) | ||
436 | { | ||
437 | if (mask & 0xfffc0000) | ||
438 | printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); | ||
439 | |||
440 | if ((mask & 0x0003ffff) && asoutput) | ||
441 | dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); | ||
442 | else if ((mask & 0x0003ffff) && !asoutput) | ||
443 | dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); | ||
444 | |||
445 | } | ||
446 | |||
447 | static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) | ||
448 | { | ||
449 | if (state) | ||
450 | dm1105_gpio_enable(dev, line, 0); | ||
451 | else { | ||
452 | dm1105_gpio_enable(dev, line, 1); | ||
453 | dm1105_gpio_clear(dev, line); | ||
454 | } | ||
455 | } | ||
456 | |||
457 | static void dm1105_setsda(void *data, int state) | ||
458 | { | ||
459 | struct dm1105_dev *dev = data; | ||
460 | |||
461 | dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); | ||
462 | } | ||
463 | |||
464 | static void dm1105_setscl(void *data, int state) | ||
465 | { | ||
466 | struct dm1105_dev *dev = data; | ||
467 | |||
468 | dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); | ||
469 | } | ||
470 | |||
471 | static int dm1105_getsda(void *data) | ||
472 | { | ||
473 | struct dm1105_dev *dev = data; | ||
474 | |||
475 | return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) | ||
476 | ? 1 : 0; | ||
477 | } | ||
478 | |||
479 | static int dm1105_getscl(void *data) | ||
480 | { | ||
481 | struct dm1105_dev *dev = data; | ||
482 | |||
483 | return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) | ||
484 | ? 1 : 0; | ||
485 | } | ||
486 | |||
331 | static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, | 487 | static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, |
332 | struct i2c_msg *msgs, int num) | 488 | struct i2c_msg *msgs, int num) |
333 | { | 489 | { |
@@ -436,31 +592,20 @@ static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) | |||
436 | static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) | 592 | static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) |
437 | { | 593 | { |
438 | struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); | 594 | struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); |
439 | u32 lnb_mask, lnb_13v, lnb_18v, lnb_off; | ||
440 | 595 | ||
441 | switch (dev->boardnr) { | 596 | dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); |
442 | case DM1105_BOARD_AXESS_DM05: | ||
443 | lnb_mask = DM05_LNB_MASK; | ||
444 | lnb_off = DM05_LNB_OFF; | ||
445 | lnb_13v = DM05_LNB_13V; | ||
446 | lnb_18v = DM05_LNB_18V; | ||
447 | break; | ||
448 | case DM1105_BOARD_DVBWORLD_2002: | ||
449 | case DM1105_BOARD_DVBWORLD_2004: | ||
450 | default: | ||
451 | lnb_mask = DM1105_LNB_MASK; | ||
452 | lnb_off = DM1105_LNB_OFF; | ||
453 | lnb_13v = DM1105_LNB_13V; | ||
454 | lnb_18v = DM1105_LNB_18V; | ||
455 | } | ||
456 | |||
457 | dm_writel(DM1105_GPIOCTR, lnb_mask); | ||
458 | if (voltage == SEC_VOLTAGE_18) | 597 | if (voltage == SEC_VOLTAGE_18) |
459 | dm_writel(DM1105_GPIOVAL, lnb_18v); | 598 | dm1105_gpio_andor(dev, |
599 | dm1105_boards[dev->boardnr].lnb.mask, | ||
600 | dm1105_boards[dev->boardnr].lnb.v18); | ||
460 | else if (voltage == SEC_VOLTAGE_13) | 601 | else if (voltage == SEC_VOLTAGE_13) |
461 | dm_writel(DM1105_GPIOVAL, lnb_13v); | 602 | dm1105_gpio_andor(dev, |
603 | dm1105_boards[dev->boardnr].lnb.mask, | ||
604 | dm1105_boards[dev->boardnr].lnb.v13); | ||
462 | else | 605 | else |
463 | dm_writel(DM1105_GPIOVAL, lnb_off); | 606 | dm1105_gpio_andor(dev, |
607 | dm1105_boards[dev->boardnr].lnb.mask, | ||
608 | dm1105_boards[dev->boardnr].lnb.off); | ||
464 | 609 | ||
465 | return 0; | 610 | return 0; |
466 | } | 611 | } |
@@ -708,6 +853,38 @@ static int __devinit frontend_init(struct dm1105_dev *dev) | |||
708 | int ret; | 853 | int ret; |
709 | 854 | ||
710 | switch (dev->boardnr) { | 855 | switch (dev->boardnr) { |
856 | case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: | ||
857 | dm1105_gpio_enable(dev, GPIO15, 1); | ||
858 | dm1105_gpio_clear(dev, GPIO15); | ||
859 | msleep(100); | ||
860 | dm1105_gpio_set(dev, GPIO15); | ||
861 | msleep(200); | ||
862 | dev->fe = dvb_attach( | ||
863 | stv0299_attach, &sharp_z0194a_config, | ||
864 | &dev->i2c_bb_adap); | ||
865 | if (dev->fe) { | ||
866 | dev->fe->ops.set_voltage = dm1105_set_voltage; | ||
867 | dvb_attach(dvb_pll_attach, dev->fe, 0x60, | ||
868 | &dev->i2c_bb_adap, DVB_PLL_OPERA1); | ||
869 | break; | ||
870 | } | ||
871 | |||
872 | dev->fe = dvb_attach( | ||
873 | stv0288_attach, &earda_config, | ||
874 | &dev->i2c_bb_adap); | ||
875 | if (dev->fe) { | ||
876 | dev->fe->ops.set_voltage = dm1105_set_voltage; | ||
877 | dvb_attach(stb6000_attach, dev->fe, 0x61, | ||
878 | &dev->i2c_bb_adap); | ||
879 | break; | ||
880 | } | ||
881 | |||
882 | dev->fe = dvb_attach( | ||
883 | si21xx_attach, &serit_config, | ||
884 | &dev->i2c_bb_adap); | ||
885 | if (dev->fe) | ||
886 | dev->fe->ops.set_voltage = dm1105_set_voltage; | ||
887 | break; | ||
711 | case DM1105_BOARD_DVBWORLD_2004: | 888 | case DM1105_BOARD_DVBWORLD_2004: |
712 | dev->fe = dvb_attach( | 889 | dev->fe = dvb_attach( |
713 | cx24116_attach, &serit_sp2633_config, | 890 | cx24116_attach, &serit_sp2633_config, |
@@ -870,11 +1047,32 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, | |||
870 | if (ret < 0) | 1047 | if (ret < 0) |
871 | goto err_dm1105_hw_exit; | 1048 | goto err_dm1105_hw_exit; |
872 | 1049 | ||
1050 | i2c_set_adapdata(&dev->i2c_bb_adap, dev); | ||
1051 | strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); | ||
1052 | dev->i2c_bb_adap.owner = THIS_MODULE; | ||
1053 | dev->i2c_bb_adap.dev.parent = &pdev->dev; | ||
1054 | dev->i2c_bb_adap.algo_data = &dev->i2c_bit; | ||
1055 | dev->i2c_bit.data = dev; | ||
1056 | dev->i2c_bit.setsda = dm1105_setsda; | ||
1057 | dev->i2c_bit.setscl = dm1105_setscl; | ||
1058 | dev->i2c_bit.getsda = dm1105_getsda; | ||
1059 | dev->i2c_bit.getscl = dm1105_getscl; | ||
1060 | dev->i2c_bit.udelay = 10; | ||
1061 | dev->i2c_bit.timeout = 10; | ||
1062 | |||
1063 | /* Raise SCL and SDA */ | ||
1064 | dm1105_setsda(dev, 1); | ||
1065 | dm1105_setscl(dev, 1); | ||
1066 | |||
1067 | ret = i2c_bit_add_bus(&dev->i2c_bb_adap); | ||
1068 | if (ret < 0) | ||
1069 | goto err_i2c_del_adapter; | ||
1070 | |||
873 | /* dvb */ | 1071 | /* dvb */ |
874 | ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, | 1072 | ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, |
875 | THIS_MODULE, &pdev->dev, adapter_nr); | 1073 | THIS_MODULE, &pdev->dev, adapter_nr); |
876 | if (ret < 0) | 1074 | if (ret < 0) |
877 | goto err_i2c_del_adapter; | 1075 | goto err_i2c_del_adapters; |
878 | 1076 | ||
879 | dvb_adapter = &dev->dvb_adapter; | 1077 | dvb_adapter = &dev->dvb_adapter; |
880 | 1078 | ||
@@ -952,6 +1150,8 @@ err_dvb_dmx_release: | |||
952 | dvb_dmx_release(dvbdemux); | 1150 | dvb_dmx_release(dvbdemux); |
953 | err_dvb_unregister_adapter: | 1151 | err_dvb_unregister_adapter: |
954 | dvb_unregister_adapter(dvb_adapter); | 1152 | dvb_unregister_adapter(dvb_adapter); |
1153 | err_i2c_del_adapters: | ||
1154 | i2c_del_adapter(&dev->i2c_bb_adap); | ||
955 | err_i2c_del_adapter: | 1155 | err_i2c_del_adapter: |
956 | i2c_del_adapter(&dev->i2c_adap); | 1156 | i2c_del_adapter(&dev->i2c_adap); |
957 | err_dm1105_hw_exit: | 1157 | err_dm1105_hw_exit: |
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index f36f471deae2..37b146961ae2 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c | |||
@@ -207,17 +207,6 @@ static int lme2510_stream_restart(struct dvb_usb_device *d) | |||
207 | rbuff, sizeof(rbuff)); | 207 | rbuff, sizeof(rbuff)); |
208 | return ret; | 208 | return ret; |
209 | } | 209 | } |
210 | static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u32 keypress) | ||
211 | { | ||
212 | struct dvb_usb_device *d = adap->dev; | ||
213 | |||
214 | deb_info(1, "INT Key Keypress =%04x", keypress); | ||
215 | |||
216 | if (keypress > 0) | ||
217 | rc_keydown(d->rc_dev, keypress, 0); | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | 210 | ||
222 | static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) | 211 | static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) |
223 | { | 212 | { |
@@ -256,6 +245,7 @@ static void lme2510_int_response(struct urb *lme_urb) | |||
256 | struct lme2510_state *st = adap->dev->priv; | 245 | struct lme2510_state *st = adap->dev->priv; |
257 | static u8 *ibuf, *rbuf; | 246 | static u8 *ibuf, *rbuf; |
258 | int i = 0, offset; | 247 | int i = 0, offset; |
248 | u32 key; | ||
259 | 249 | ||
260 | switch (lme_urb->status) { | 250 | switch (lme_urb->status) { |
261 | case 0: | 251 | case 0: |
@@ -282,10 +272,16 @@ static void lme2510_int_response(struct urb *lme_urb) | |||
282 | 272 | ||
283 | switch (ibuf[0]) { | 273 | switch (ibuf[0]) { |
284 | case 0xaa: | 274 | case 0xaa: |
285 | debug_data_snipet(1, "INT Remote data snipet in", ibuf); | 275 | debug_data_snipet(1, "INT Remote data snipet", ibuf); |
286 | lme2510_remote_keypress(adap, | 276 | if ((ibuf[4] + ibuf[5]) == 0xff) { |
287 | (u32)(ibuf[2] << 24) + (ibuf[3] << 16) + | 277 | key = ibuf[5]; |
288 | (ibuf[4] << 8) + ibuf[5]); | 278 | key += (ibuf[3] > 0) |
279 | ? (ibuf[3] ^ 0xff) << 8 : 0; | ||
280 | key += (ibuf[2] ^ 0xff) << 16; | ||
281 | deb_info(1, "INT Key =%08x", key); | ||
282 | if (adap->dev->rc_dev != NULL) | ||
283 | rc_keydown(adap->dev->rc_dev, key, 0); | ||
284 | } | ||
289 | break; | 285 | break; |
290 | case 0xbb: | 286 | case 0xbb: |
291 | switch (st->tuner_config) { | 287 | switch (st->tuner_config) { |
@@ -691,45 +687,6 @@ static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) | |||
691 | return (ret < 0) ? -ENODEV : 0; | 687 | return (ret < 0) ? -ENODEV : 0; |
692 | } | 688 | } |
693 | 689 | ||
694 | static int lme2510_int_service(struct dvb_usb_adapter *adap) | ||
695 | { | ||
696 | struct dvb_usb_device *d = adap->dev; | ||
697 | struct rc_dev *rc; | ||
698 | int ret; | ||
699 | |||
700 | info("STA Configuring Remote"); | ||
701 | |||
702 | rc = rc_allocate_device(); | ||
703 | if (!rc) | ||
704 | return -ENOMEM; | ||
705 | |||
706 | usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); | ||
707 | strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); | ||
708 | |||
709 | rc->input_name = "LME2510 Remote Control"; | ||
710 | rc->input_phys = d->rc_phys; | ||
711 | rc->map_name = RC_MAP_LME2510; | ||
712 | rc->driver_name = "LME 2510"; | ||
713 | usb_to_input_id(d->udev, &rc->input_id); | ||
714 | |||
715 | ret = rc_register_device(rc); | ||
716 | if (ret) { | ||
717 | rc_free_device(rc); | ||
718 | return ret; | ||
719 | } | ||
720 | d->rc_dev = rc; | ||
721 | |||
722 | /* Start the Interrupt */ | ||
723 | ret = lme2510_int_read(adap); | ||
724 | if (ret < 0) { | ||
725 | rc_unregister_device(rc); | ||
726 | info("INT Unable to start Interrupt Service"); | ||
727 | return -ENODEV; | ||
728 | } | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static u8 check_sum(u8 *p, u8 len) | 690 | static u8 check_sum(u8 *p, u8 len) |
734 | { | 691 | { |
735 | u8 sum = 0; | 692 | u8 sum = 0; |
@@ -831,7 +788,7 @@ static int lme_firmware_switch(struct usb_device *udev, int cold) | |||
831 | 788 | ||
832 | cold_fw = !cold; | 789 | cold_fw = !cold; |
833 | 790 | ||
834 | if (udev->descriptor.idProduct == 0x1122) { | 791 | if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) { |
835 | switch (dvb_usb_lme2510_firmware) { | 792 | switch (dvb_usb_lme2510_firmware) { |
836 | default: | 793 | default: |
837 | dvb_usb_lme2510_firmware = TUNER_S0194; | 794 | dvb_usb_lme2510_firmware = TUNER_S0194; |
@@ -1053,8 +1010,11 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) | |||
1053 | 1010 | ||
1054 | 1011 | ||
1055 | end: if (ret) { | 1012 | end: if (ret) { |
1056 | kfree(adap->fe); | 1013 | if (adap->fe) { |
1057 | adap->fe = NULL; | 1014 | dvb_frontend_detach(adap->fe); |
1015 | adap->fe = NULL; | ||
1016 | } | ||
1017 | adap->dev->props.rc.core.rc_codes = NULL; | ||
1058 | return -ENODEV; | 1018 | return -ENODEV; |
1059 | } | 1019 | } |
1060 | 1020 | ||
@@ -1097,8 +1057,12 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) | |||
1097 | return -ENODEV; | 1057 | return -ENODEV; |
1098 | } | 1058 | } |
1099 | 1059 | ||
1100 | /* Start the Interrupt & Remote*/ | 1060 | /* Start the Interrupt*/ |
1101 | ret = lme2510_int_service(adap); | 1061 | ret = lme2510_int_read(adap); |
1062 | if (ret < 0) { | ||
1063 | info("INT Unable to start Interrupt Service"); | ||
1064 | return -ENODEV; | ||
1065 | } | ||
1102 | 1066 | ||
1103 | return ret; | 1067 | return ret; |
1104 | } | 1068 | } |
@@ -1204,6 +1168,12 @@ static struct dvb_usb_device_properties lme2510_properties = { | |||
1204 | } | 1168 | } |
1205 | } | 1169 | } |
1206 | }, | 1170 | }, |
1171 | .rc.core = { | ||
1172 | .protocol = RC_TYPE_NEC, | ||
1173 | .module_name = "LME2510 Remote Control", | ||
1174 | .allowed_protos = RC_TYPE_NEC, | ||
1175 | .rc_codes = RC_MAP_LME2510, | ||
1176 | }, | ||
1207 | .power_ctrl = lme2510_powerup, | 1177 | .power_ctrl = lme2510_powerup, |
1208 | .identify_state = lme2510_identify_state, | 1178 | .identify_state = lme2510_identify_state, |
1209 | .i2c_algo = &lme2510_i2c_algo, | 1179 | .i2c_algo = &lme2510_i2c_algo, |
@@ -1246,6 +1216,12 @@ static struct dvb_usb_device_properties lme2510c_properties = { | |||
1246 | } | 1216 | } |
1247 | } | 1217 | } |
1248 | }, | 1218 | }, |
1219 | .rc.core = { | ||
1220 | .protocol = RC_TYPE_NEC, | ||
1221 | .module_name = "LME2510 Remote Control", | ||
1222 | .allowed_protos = RC_TYPE_NEC, | ||
1223 | .rc_codes = RC_MAP_LME2510, | ||
1224 | }, | ||
1249 | .power_ctrl = lme2510_powerup, | 1225 | .power_ctrl = lme2510_powerup, |
1250 | .identify_state = lme2510_identify_state, | 1226 | .identify_state = lme2510_identify_state, |
1251 | .i2c_algo = &lme2510_i2c_algo, | 1227 | .i2c_algo = &lme2510_i2c_algo, |
@@ -1269,19 +1245,21 @@ static void *lme2510_exit_int(struct dvb_usb_device *d) | |||
1269 | adap->feedcount = 0; | 1245 | adap->feedcount = 0; |
1270 | } | 1246 | } |
1271 | 1247 | ||
1272 | if (st->lme_urb != NULL) { | 1248 | if (st->usb_buffer != NULL) { |
1273 | st->i2c_talk_onoff = 1; | 1249 | st->i2c_talk_onoff = 1; |
1274 | st->signal_lock = 0; | 1250 | st->signal_lock = 0; |
1275 | st->signal_level = 0; | 1251 | st->signal_level = 0; |
1276 | st->signal_sn = 0; | 1252 | st->signal_sn = 0; |
1277 | buffer = st->usb_buffer; | 1253 | buffer = st->usb_buffer; |
1254 | } | ||
1255 | |||
1256 | if (st->lme_urb != NULL) { | ||
1278 | usb_kill_urb(st->lme_urb); | 1257 | usb_kill_urb(st->lme_urb); |
1279 | usb_free_coherent(d->udev, 5000, st->buffer, | 1258 | usb_free_coherent(d->udev, 5000, st->buffer, |
1280 | st->lme_urb->transfer_dma); | 1259 | st->lme_urb->transfer_dma); |
1281 | info("Interrupt Service Stopped"); | 1260 | info("Interrupt Service Stopped"); |
1282 | rc_unregister_device(d->rc_dev); | ||
1283 | info("Remote Stopped"); | ||
1284 | } | 1261 | } |
1262 | |||
1285 | return buffer; | 1263 | return buffer; |
1286 | } | 1264 | } |
1287 | 1265 | ||
@@ -1293,7 +1271,8 @@ static void lme2510_exit(struct usb_interface *intf) | |||
1293 | if (d != NULL) { | 1271 | if (d != NULL) { |
1294 | usb_buffer = lme2510_exit_int(d); | 1272 | usb_buffer = lme2510_exit_int(d); |
1295 | dvb_usb_device_exit(intf); | 1273 | dvb_usb_device_exit(intf); |
1296 | kfree(usb_buffer); | 1274 | if (usb_buffer != NULL) |
1275 | kfree(usb_buffer); | ||
1297 | } | 1276 | } |
1298 | } | 1277 | } |
1299 | 1278 | ||
@@ -1327,5 +1306,5 @@ module_exit(lme2510_module_exit); | |||
1327 | 1306 | ||
1328 | MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); | 1307 | MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); |
1329 | MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); | 1308 | MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); |
1330 | MODULE_VERSION("1.86"); | 1309 | MODULE_VERSION("1.88"); |
1331 | MODULE_LICENSE("GPL"); | 1310 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c index 2da55ec20392..d70eee00f33a 100644 --- a/drivers/media/dvb/frontends/stb0899_algo.c +++ b/drivers/media/dvb/frontends/stb0899_algo.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include "stb0899_priv.h" | 23 | #include "stb0899_priv.h" |
24 | #include "stb0899_reg.h" | 24 | #include "stb0899_reg.h" |
25 | 25 | ||
26 | inline u32 stb0899_do_div(u64 n, u32 d) | 26 | static inline u32 stb0899_do_div(u64 n, u32 d) |
27 | { | 27 | { |
28 | /* wrap do_div() for ease of use */ | 28 | /* wrap do_div() for ease of use */ |
29 | 29 | ||
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c index 1742056a34e8..53c7d8f1df28 100644 --- a/drivers/media/dvb/frontends/tda8261.c +++ b/drivers/media/dvb/frontends/tda8261.c | |||
@@ -224,7 +224,6 @@ exit: | |||
224 | } | 224 | } |
225 | 225 | ||
226 | EXPORT_SYMBOL(tda8261_attach); | 226 | EXPORT_SYMBOL(tda8261_attach); |
227 | MODULE_PARM_DESC(verbose, "Set verbosity level"); | ||
228 | 227 | ||
229 | MODULE_AUTHOR("Manu Abraham"); | 228 | MODULE_AUTHOR("Manu Abraham"); |
230 | MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); | 229 | MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); |
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 46cacf845049..459f7272d326 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c | |||
@@ -1382,7 +1382,7 @@ static int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | |||
1382 | 1382 | ||
1383 | switch (ctrl->id) { | 1383 | switch (ctrl->id) { |
1384 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | 1384 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: |
1385 | ctrl->val = wl1273_fm_get_tx_ctune(radio); | 1385 | ctrl->cur.val = wl1273_fm_get_tx_ctune(radio); |
1386 | break; | 1386 | break; |
1387 | 1387 | ||
1388 | default: | 1388 | default: |
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index d50e5ac75ab6..87010724f914 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c | |||
@@ -191,7 +191,7 @@ static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | |||
191 | 191 | ||
192 | switch (ctrl->id) { | 192 | switch (ctrl->id) { |
193 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | 193 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: |
194 | ctrl->val = fm_tx_get_tune_cap_val(fmdev); | 194 | ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev); |
195 | break; | 195 | break; |
196 | default: | 196 | default: |
197 | fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); | 197 | fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); |
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 154c337f00fd..7d4bbc226d06 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig | |||
@@ -148,6 +148,18 @@ config IR_ITE_CIR | |||
148 | To compile this driver as a module, choose M here: the | 148 | To compile this driver as a module, choose M here: the |
149 | module will be called ite-cir. | 149 | module will be called ite-cir. |
150 | 150 | ||
151 | config IR_FINTEK | ||
152 | tristate "Fintek Consumer Infrared Transceiver" | ||
153 | depends on PNP | ||
154 | depends on RC_CORE | ||
155 | ---help--- | ||
156 | Say Y here to enable support for integrated infrared receiver | ||
157 | /transciever made by Fintek. This chip is found on assorted | ||
158 | Jetway motherboards (and of course, possibly others). | ||
159 | |||
160 | To compile this driver as a module, choose M here: the | ||
161 | module will be called fintek-cir. | ||
162 | |||
151 | config IR_NUVOTON | 163 | config IR_NUVOTON |
152 | tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" | 164 | tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" |
153 | depends on PNP | 165 | depends on PNP |
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 1f90a219a162..52830e5f4eaa 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile | |||
@@ -16,6 +16,7 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o | |||
16 | obj-$(CONFIG_IR_IMON) += imon.o | 16 | obj-$(CONFIG_IR_IMON) += imon.o |
17 | obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o | 17 | obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o |
18 | obj-$(CONFIG_IR_MCEUSB) += mceusb.o | 18 | obj-$(CONFIG_IR_MCEUSB) += mceusb.o |
19 | obj-$(CONFIG_IR_FINTEK) += fintek-cir.o | ||
19 | obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o | 20 | obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o |
20 | obj-$(CONFIG_IR_ENE) += ene_ir.o | 21 | obj-$(CONFIG_IR_ENE) += ene_ir.o |
21 | obj-$(CONFIG_IR_REDRAT3) += redrat3.o | 22 | obj-$(CONFIG_IR_REDRAT3) += redrat3.o |
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c new file mode 100644 index 000000000000..8fa539dde1b4 --- /dev/null +++ b/drivers/media/rc/fintek-cir.c | |||
@@ -0,0 +1,684 @@ | |||
1 | /* | ||
2 | * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR | ||
3 | * | ||
4 | * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> | ||
5 | * | ||
6 | * Special thanks to Fintek for providing hardware and spec sheets. | ||
7 | * This driver is based upon the nuvoton, ite and ene drivers for | ||
8 | * similar hardware. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation; either version 2 of the | ||
13 | * License, or (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
23 | * USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/pnp.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <media/rc-core.h> | ||
34 | #include <linux/pci_ids.h> | ||
35 | |||
36 | #include "fintek-cir.h" | ||
37 | |||
38 | /* write val to config reg */ | ||
39 | static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg) | ||
40 | { | ||
41 | fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", | ||
42 | __func__, reg, val, fintek->cr_ip, fintek->cr_dp); | ||
43 | outb(reg, fintek->cr_ip); | ||
44 | outb(val, fintek->cr_dp); | ||
45 | } | ||
46 | |||
47 | /* read val from config reg */ | ||
48 | static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg) | ||
49 | { | ||
50 | u8 val; | ||
51 | |||
52 | outb(reg, fintek->cr_ip); | ||
53 | val = inb(fintek->cr_dp); | ||
54 | |||
55 | fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", | ||
56 | __func__, reg, val, fintek->cr_ip, fintek->cr_dp); | ||
57 | return val; | ||
58 | } | ||
59 | |||
60 | /* update config register bit without changing other bits */ | ||
61 | static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) | ||
62 | { | ||
63 | u8 tmp = fintek_cr_read(fintek, reg) | val; | ||
64 | fintek_cr_write(fintek, tmp, reg); | ||
65 | } | ||
66 | |||
67 | /* clear config register bit without changing other bits */ | ||
68 | static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) | ||
69 | { | ||
70 | u8 tmp = fintek_cr_read(fintek, reg) & ~val; | ||
71 | fintek_cr_write(fintek, tmp, reg); | ||
72 | } | ||
73 | |||
74 | /* enter config mode */ | ||
75 | static inline void fintek_config_mode_enable(struct fintek_dev *fintek) | ||
76 | { | ||
77 | /* Enabling Config Mode explicitly requires writing 2x */ | ||
78 | outb(CONFIG_REG_ENABLE, fintek->cr_ip); | ||
79 | outb(CONFIG_REG_ENABLE, fintek->cr_ip); | ||
80 | } | ||
81 | |||
82 | /* exit config mode */ | ||
83 | static inline void fintek_config_mode_disable(struct fintek_dev *fintek) | ||
84 | { | ||
85 | outb(CONFIG_REG_DISABLE, fintek->cr_ip); | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * When you want to address a specific logical device, write its logical | ||
90 | * device number to GCR_LOGICAL_DEV_NO | ||
91 | */ | ||
92 | static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev) | ||
93 | { | ||
94 | fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO); | ||
95 | } | ||
96 | |||
97 | /* write val to cir config register */ | ||
98 | static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset) | ||
99 | { | ||
100 | outb(val, fintek->cir_addr + offset); | ||
101 | } | ||
102 | |||
103 | /* read val from cir config register */ | ||
104 | static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset) | ||
105 | { | ||
106 | u8 val; | ||
107 | |||
108 | val = inb(fintek->cir_addr + offset); | ||
109 | |||
110 | return val; | ||
111 | } | ||
112 | |||
113 | #define pr_reg(text, ...) \ | ||
114 | printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__) | ||
115 | |||
116 | /* dump current cir register contents */ | ||
117 | static void cir_dump_regs(struct fintek_dev *fintek) | ||
118 | { | ||
119 | fintek_config_mode_enable(fintek); | ||
120 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
121 | |||
122 | pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); | ||
123 | pr_reg(" * CR CIR BASE ADDR: 0x%x\n", | ||
124 | (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | | ||
125 | fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO)); | ||
126 | pr_reg(" * CR CIR IRQ NUM: 0x%x\n", | ||
127 | fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); | ||
128 | |||
129 | fintek_config_mode_disable(fintek); | ||
130 | |||
131 | pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); | ||
132 | pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS)); | ||
133 | pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL)); | ||
134 | pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA)); | ||
135 | pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); | ||
136 | pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA)); | ||
137 | } | ||
138 | |||
139 | /* detect hardware features */ | ||
140 | static int fintek_hw_detect(struct fintek_dev *fintek) | ||
141 | { | ||
142 | unsigned long flags; | ||
143 | u8 chip_major, chip_minor; | ||
144 | u8 vendor_major, vendor_minor; | ||
145 | u8 portsel, ir_class; | ||
146 | u16 vendor; | ||
147 | int ret = 0; | ||
148 | |||
149 | fintek_config_mode_enable(fintek); | ||
150 | |||
151 | /* Check if we're using config port 0x4e or 0x2e */ | ||
152 | portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); | ||
153 | if (portsel == 0xff) { | ||
154 | fit_pr(KERN_INFO, "first portsel read was bunk, trying alt"); | ||
155 | fintek_config_mode_disable(fintek); | ||
156 | fintek->cr_ip = CR_INDEX_PORT2; | ||
157 | fintek->cr_dp = CR_DATA_PORT2; | ||
158 | fintek_config_mode_enable(fintek); | ||
159 | portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); | ||
160 | } | ||
161 | fit_dbg("portsel reg: 0x%02x", portsel); | ||
162 | |||
163 | ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS); | ||
164 | fit_dbg("ir_class reg: 0x%02x", ir_class); | ||
165 | |||
166 | switch (ir_class) { | ||
167 | case CLASS_RX_2TX: | ||
168 | case CLASS_RX_1TX: | ||
169 | fintek->hw_tx_capable = true; | ||
170 | break; | ||
171 | case CLASS_RX_ONLY: | ||
172 | default: | ||
173 | fintek->hw_tx_capable = false; | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); | ||
178 | chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); | ||
179 | |||
180 | vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); | ||
181 | vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); | ||
182 | vendor = vendor_major << 8 | vendor_minor; | ||
183 | |||
184 | if (vendor != VENDOR_ID_FINTEK) | ||
185 | fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor); | ||
186 | else | ||
187 | fit_dbg("Read Fintek vendor ID from chip"); | ||
188 | |||
189 | fintek_config_mode_disable(fintek); | ||
190 | |||
191 | spin_lock_irqsave(&fintek->fintek_lock, flags); | ||
192 | fintek->chip_major = chip_major; | ||
193 | fintek->chip_minor = chip_minor; | ||
194 | fintek->chip_vendor = vendor; | ||
195 | spin_unlock_irqrestore(&fintek->fintek_lock, flags); | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | static void fintek_cir_ldev_init(struct fintek_dev *fintek) | ||
201 | { | ||
202 | /* Select CIR logical device and enable */ | ||
203 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
204 | fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); | ||
205 | |||
206 | /* Write allocated CIR address and IRQ information to hardware */ | ||
207 | fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI); | ||
208 | fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO); | ||
209 | |||
210 | fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL); | ||
211 | |||
212 | fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)", | ||
213 | fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len); | ||
214 | } | ||
215 | |||
216 | /* enable CIR interrupts */ | ||
217 | static void fintek_enable_cir_irq(struct fintek_dev *fintek) | ||
218 | { | ||
219 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); | ||
220 | } | ||
221 | |||
222 | static void fintek_cir_regs_init(struct fintek_dev *fintek) | ||
223 | { | ||
224 | /* clear any and all stray interrupts */ | ||
225 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); | ||
226 | |||
227 | /* and finally, enable interrupts */ | ||
228 | fintek_enable_cir_irq(fintek); | ||
229 | } | ||
230 | |||
231 | static void fintek_enable_wake(struct fintek_dev *fintek) | ||
232 | { | ||
233 | fintek_config_mode_enable(fintek); | ||
234 | fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI); | ||
235 | |||
236 | /* Allow CIR PME's to wake system */ | ||
237 | fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG); | ||
238 | /* Enable CIR PME's */ | ||
239 | fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG); | ||
240 | /* Clear CIR PME status register */ | ||
241 | fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG); | ||
242 | /* Save state */ | ||
243 | fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG); | ||
244 | |||
245 | fintek_config_mode_disable(fintek); | ||
246 | } | ||
247 | |||
248 | static int fintek_cmdsize(u8 cmd, u8 subcmd) | ||
249 | { | ||
250 | int datasize = 0; | ||
251 | |||
252 | switch (cmd) { | ||
253 | case BUF_COMMAND_NULL: | ||
254 | if (subcmd == BUF_HW_CMD_HEADER) | ||
255 | datasize = 1; | ||
256 | break; | ||
257 | case BUF_HW_CMD_HEADER: | ||
258 | if (subcmd == BUF_CMD_G_REVISION) | ||
259 | datasize = 2; | ||
260 | break; | ||
261 | case BUF_COMMAND_HEADER: | ||
262 | switch (subcmd) { | ||
263 | case BUF_CMD_S_CARRIER: | ||
264 | case BUF_CMD_S_TIMEOUT: | ||
265 | case BUF_RSP_PULSE_COUNT: | ||
266 | datasize = 2; | ||
267 | break; | ||
268 | case BUF_CMD_SIG_END: | ||
269 | case BUF_CMD_S_TXMASK: | ||
270 | case BUF_CMD_S_RXSENSOR: | ||
271 | datasize = 1; | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | return datasize; | ||
277 | } | ||
278 | |||
279 | /* process ir data stored in driver buffer */ | ||
280 | static void fintek_process_rx_ir_data(struct fintek_dev *fintek) | ||
281 | { | ||
282 | DEFINE_IR_RAW_EVENT(rawir); | ||
283 | u8 sample; | ||
284 | int i; | ||
285 | |||
286 | for (i = 0; i < fintek->pkts; i++) { | ||
287 | sample = fintek->buf[i]; | ||
288 | switch (fintek->parser_state) { | ||
289 | case CMD_HEADER: | ||
290 | fintek->cmd = sample; | ||
291 | if ((fintek->cmd == BUF_COMMAND_HEADER) || | ||
292 | ((fintek->cmd & BUF_COMMAND_MASK) != | ||
293 | BUF_PULSE_BIT)) { | ||
294 | fintek->parser_state = SUBCMD; | ||
295 | continue; | ||
296 | } | ||
297 | fintek->rem = (fintek->cmd & BUF_LEN_MASK); | ||
298 | fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem); | ||
299 | if (fintek->rem) | ||
300 | fintek->parser_state = PARSE_IRDATA; | ||
301 | else | ||
302 | ir_raw_event_reset(fintek->rdev); | ||
303 | break; | ||
304 | case SUBCMD: | ||
305 | fintek->rem = fintek_cmdsize(fintek->cmd, sample); | ||
306 | fintek->parser_state = CMD_DATA; | ||
307 | break; | ||
308 | case CMD_DATA: | ||
309 | fintek->rem--; | ||
310 | break; | ||
311 | case PARSE_IRDATA: | ||
312 | fintek->rem--; | ||
313 | init_ir_raw_event(&rawir); | ||
314 | rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); | ||
315 | rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK) | ||
316 | * CIR_SAMPLE_PERIOD); | ||
317 | |||
318 | fit_dbg("Storing %s with duration %d", | ||
319 | rawir.pulse ? "pulse" : "space", | ||
320 | rawir.duration); | ||
321 | ir_raw_event_store_with_filter(fintek->rdev, &rawir); | ||
322 | break; | ||
323 | } | ||
324 | |||
325 | if ((fintek->parser_state != CMD_HEADER) && !fintek->rem) | ||
326 | fintek->parser_state = CMD_HEADER; | ||
327 | } | ||
328 | |||
329 | fintek->pkts = 0; | ||
330 | |||
331 | fit_dbg("Calling ir_raw_event_handle"); | ||
332 | ir_raw_event_handle(fintek->rdev); | ||
333 | } | ||
334 | |||
335 | /* copy data from hardware rx register into driver buffer */ | ||
336 | static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs) | ||
337 | { | ||
338 | unsigned long flags; | ||
339 | u8 sample, status; | ||
340 | |||
341 | spin_lock_irqsave(&fintek->fintek_lock, flags); | ||
342 | |||
343 | /* | ||
344 | * We must read data from CIR_RX_DATA until the hardware IR buffer | ||
345 | * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in | ||
346 | * the CIR_STATUS register | ||
347 | */ | ||
348 | do { | ||
349 | sample = fintek_cir_reg_read(fintek, CIR_RX_DATA); | ||
350 | fit_dbg("%s: sample: 0x%02x", __func__, sample); | ||
351 | |||
352 | fintek->buf[fintek->pkts] = sample; | ||
353 | fintek->pkts++; | ||
354 | |||
355 | status = fintek_cir_reg_read(fintek, CIR_STATUS); | ||
356 | if (!(status & CIR_STATUS_IRQ_EN)) | ||
357 | break; | ||
358 | } while (status & rx_irqs); | ||
359 | |||
360 | fintek_process_rx_ir_data(fintek); | ||
361 | |||
362 | spin_unlock_irqrestore(&fintek->fintek_lock, flags); | ||
363 | } | ||
364 | |||
365 | static void fintek_cir_log_irqs(u8 status) | ||
366 | { | ||
367 | fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status, | ||
368 | status & CIR_STATUS_IRQ_EN ? " IRQEN" : "", | ||
369 | status & CIR_STATUS_TX_FINISH ? " TXF" : "", | ||
370 | status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "", | ||
371 | status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "", | ||
372 | status & CIR_STATUS_RX_RECEIVE ? " RXOK" : ""); | ||
373 | } | ||
374 | |||
375 | /* interrupt service routine for incoming and outgoing CIR data */ | ||
376 | static irqreturn_t fintek_cir_isr(int irq, void *data) | ||
377 | { | ||
378 | struct fintek_dev *fintek = data; | ||
379 | u8 status, rx_irqs; | ||
380 | |||
381 | fit_dbg_verbose("%s firing", __func__); | ||
382 | |||
383 | fintek_config_mode_enable(fintek); | ||
384 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
385 | fintek_config_mode_disable(fintek); | ||
386 | |||
387 | /* | ||
388 | * Get IR Status register contents. Write 1 to ack/clear | ||
389 | * | ||
390 | * bit: reg name - description | ||
391 | * 3: TX_FINISH - TX is finished | ||
392 | * 2: TX_UNDERRUN - TX underrun | ||
393 | * 1: RX_TIMEOUT - RX data timeout | ||
394 | * 0: RX_RECEIVE - RX data received | ||
395 | */ | ||
396 | status = fintek_cir_reg_read(fintek, CIR_STATUS); | ||
397 | if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) { | ||
398 | fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status); | ||
399 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); | ||
400 | return IRQ_RETVAL(IRQ_NONE); | ||
401 | } | ||
402 | |||
403 | if (debug) | ||
404 | fintek_cir_log_irqs(status); | ||
405 | |||
406 | rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT); | ||
407 | if (rx_irqs) | ||
408 | fintek_get_rx_ir_data(fintek, rx_irqs); | ||
409 | |||
410 | /* ack/clear all irq flags we've got */ | ||
411 | fintek_cir_reg_write(fintek, status, CIR_STATUS); | ||
412 | |||
413 | fit_dbg_verbose("%s done", __func__); | ||
414 | return IRQ_RETVAL(IRQ_HANDLED); | ||
415 | } | ||
416 | |||
417 | static void fintek_enable_cir(struct fintek_dev *fintek) | ||
418 | { | ||
419 | /* set IRQ enabled */ | ||
420 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); | ||
421 | |||
422 | fintek_config_mode_enable(fintek); | ||
423 | |||
424 | /* enable the CIR logical device */ | ||
425 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
426 | fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); | ||
427 | |||
428 | fintek_config_mode_disable(fintek); | ||
429 | |||
430 | /* clear all pending interrupts */ | ||
431 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); | ||
432 | |||
433 | /* enable interrupts */ | ||
434 | fintek_enable_cir_irq(fintek); | ||
435 | } | ||
436 | |||
437 | static void fintek_disable_cir(struct fintek_dev *fintek) | ||
438 | { | ||
439 | fintek_config_mode_enable(fintek); | ||
440 | |||
441 | /* disable the CIR logical device */ | ||
442 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
443 | fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); | ||
444 | |||
445 | fintek_config_mode_disable(fintek); | ||
446 | } | ||
447 | |||
448 | static int fintek_open(struct rc_dev *dev) | ||
449 | { | ||
450 | struct fintek_dev *fintek = dev->priv; | ||
451 | unsigned long flags; | ||
452 | |||
453 | spin_lock_irqsave(&fintek->fintek_lock, flags); | ||
454 | fintek_enable_cir(fintek); | ||
455 | spin_unlock_irqrestore(&fintek->fintek_lock, flags); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | static void fintek_close(struct rc_dev *dev) | ||
461 | { | ||
462 | struct fintek_dev *fintek = dev->priv; | ||
463 | unsigned long flags; | ||
464 | |||
465 | spin_lock_irqsave(&fintek->fintek_lock, flags); | ||
466 | fintek_disable_cir(fintek); | ||
467 | spin_unlock_irqrestore(&fintek->fintek_lock, flags); | ||
468 | } | ||
469 | |||
470 | /* Allocate memory, probe hardware, and initialize everything */ | ||
471 | static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) | ||
472 | { | ||
473 | struct fintek_dev *fintek; | ||
474 | struct rc_dev *rdev; | ||
475 | int ret = -ENOMEM; | ||
476 | |||
477 | fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL); | ||
478 | if (!fintek) | ||
479 | return ret; | ||
480 | |||
481 | /* input device for IR remote (and tx) */ | ||
482 | rdev = rc_allocate_device(); | ||
483 | if (!rdev) | ||
484 | goto failure; | ||
485 | |||
486 | ret = -ENODEV; | ||
487 | /* validate pnp resources */ | ||
488 | if (!pnp_port_valid(pdev, 0)) { | ||
489 | dev_err(&pdev->dev, "IR PNP Port not valid!\n"); | ||
490 | goto failure; | ||
491 | } | ||
492 | |||
493 | if (!pnp_irq_valid(pdev, 0)) { | ||
494 | dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); | ||
495 | goto failure; | ||
496 | } | ||
497 | |||
498 | fintek->cir_addr = pnp_port_start(pdev, 0); | ||
499 | fintek->cir_irq = pnp_irq(pdev, 0); | ||
500 | fintek->cir_port_len = pnp_port_len(pdev, 0); | ||
501 | |||
502 | fintek->cr_ip = CR_INDEX_PORT; | ||
503 | fintek->cr_dp = CR_DATA_PORT; | ||
504 | |||
505 | spin_lock_init(&fintek->fintek_lock); | ||
506 | |||
507 | ret = -EBUSY; | ||
508 | /* now claim resources */ | ||
509 | if (!request_region(fintek->cir_addr, | ||
510 | fintek->cir_port_len, FINTEK_DRIVER_NAME)) | ||
511 | goto failure; | ||
512 | |||
513 | if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, | ||
514 | FINTEK_DRIVER_NAME, (void *)fintek)) | ||
515 | goto failure; | ||
516 | |||
517 | pnp_set_drvdata(pdev, fintek); | ||
518 | fintek->pdev = pdev; | ||
519 | |||
520 | ret = fintek_hw_detect(fintek); | ||
521 | if (ret) | ||
522 | goto failure; | ||
523 | |||
524 | /* Initialize CIR & CIR Wake Logical Devices */ | ||
525 | fintek_config_mode_enable(fintek); | ||
526 | fintek_cir_ldev_init(fintek); | ||
527 | fintek_config_mode_disable(fintek); | ||
528 | |||
529 | /* Initialize CIR & CIR Wake Config Registers */ | ||
530 | fintek_cir_regs_init(fintek); | ||
531 | |||
532 | /* Set up the rc device */ | ||
533 | rdev->priv = fintek; | ||
534 | rdev->driver_type = RC_DRIVER_IR_RAW; | ||
535 | rdev->allowed_protos = RC_TYPE_ALL; | ||
536 | rdev->open = fintek_open; | ||
537 | rdev->close = fintek_close; | ||
538 | rdev->input_name = FINTEK_DESCRIPTION; | ||
539 | rdev->input_phys = "fintek/cir0"; | ||
540 | rdev->input_id.bustype = BUS_HOST; | ||
541 | rdev->input_id.vendor = VENDOR_ID_FINTEK; | ||
542 | rdev->input_id.product = fintek->chip_major; | ||
543 | rdev->input_id.version = fintek->chip_minor; | ||
544 | rdev->dev.parent = &pdev->dev; | ||
545 | rdev->driver_name = FINTEK_DRIVER_NAME; | ||
546 | rdev->map_name = RC_MAP_RC6_MCE; | ||
547 | rdev->timeout = US_TO_NS(1000); | ||
548 | /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ | ||
549 | rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); | ||
550 | |||
551 | ret = rc_register_device(rdev); | ||
552 | if (ret) | ||
553 | goto failure; | ||
554 | |||
555 | device_init_wakeup(&pdev->dev, true); | ||
556 | fintek->rdev = rdev; | ||
557 | fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); | ||
558 | if (debug) | ||
559 | cir_dump_regs(fintek); | ||
560 | |||
561 | return 0; | ||
562 | |||
563 | failure: | ||
564 | if (fintek->cir_irq) | ||
565 | free_irq(fintek->cir_irq, fintek); | ||
566 | if (fintek->cir_addr) | ||
567 | release_region(fintek->cir_addr, fintek->cir_port_len); | ||
568 | |||
569 | rc_free_device(rdev); | ||
570 | kfree(fintek); | ||
571 | |||
572 | return ret; | ||
573 | } | ||
574 | |||
575 | static void __devexit fintek_remove(struct pnp_dev *pdev) | ||
576 | { | ||
577 | struct fintek_dev *fintek = pnp_get_drvdata(pdev); | ||
578 | unsigned long flags; | ||
579 | |||
580 | spin_lock_irqsave(&fintek->fintek_lock, flags); | ||
581 | /* disable CIR */ | ||
582 | fintek_disable_cir(fintek); | ||
583 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); | ||
584 | /* enable CIR Wake (for IR power-on) */ | ||
585 | fintek_enable_wake(fintek); | ||
586 | spin_unlock_irqrestore(&fintek->fintek_lock, flags); | ||
587 | |||
588 | /* free resources */ | ||
589 | free_irq(fintek->cir_irq, fintek); | ||
590 | release_region(fintek->cir_addr, fintek->cir_port_len); | ||
591 | |||
592 | rc_unregister_device(fintek->rdev); | ||
593 | |||
594 | kfree(fintek); | ||
595 | } | ||
596 | |||
597 | static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) | ||
598 | { | ||
599 | struct fintek_dev *fintek = pnp_get_drvdata(pdev); | ||
600 | |||
601 | fit_dbg("%s called", __func__); | ||
602 | |||
603 | /* disable all CIR interrupts */ | ||
604 | fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); | ||
605 | |||
606 | fintek_config_mode_enable(fintek); | ||
607 | |||
608 | /* disable cir logical dev */ | ||
609 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
610 | fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); | ||
611 | |||
612 | fintek_config_mode_disable(fintek); | ||
613 | |||
614 | /* make sure wake is enabled */ | ||
615 | fintek_enable_wake(fintek); | ||
616 | |||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int fintek_resume(struct pnp_dev *pdev) | ||
621 | { | ||
622 | int ret = 0; | ||
623 | struct fintek_dev *fintek = pnp_get_drvdata(pdev); | ||
624 | |||
625 | fit_dbg("%s called", __func__); | ||
626 | |||
627 | /* open interrupt */ | ||
628 | fintek_enable_cir_irq(fintek); | ||
629 | |||
630 | /* Enable CIR logical device */ | ||
631 | fintek_config_mode_enable(fintek); | ||
632 | fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); | ||
633 | fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); | ||
634 | |||
635 | fintek_config_mode_disable(fintek); | ||
636 | |||
637 | fintek_cir_regs_init(fintek); | ||
638 | |||
639 | return ret; | ||
640 | } | ||
641 | |||
642 | static void fintek_shutdown(struct pnp_dev *pdev) | ||
643 | { | ||
644 | struct fintek_dev *fintek = pnp_get_drvdata(pdev); | ||
645 | fintek_enable_wake(fintek); | ||
646 | } | ||
647 | |||
648 | static const struct pnp_device_id fintek_ids[] = { | ||
649 | { "FIT0002", 0 }, /* CIR */ | ||
650 | { "", 0 }, | ||
651 | }; | ||
652 | |||
653 | static struct pnp_driver fintek_driver = { | ||
654 | .name = FINTEK_DRIVER_NAME, | ||
655 | .id_table = fintek_ids, | ||
656 | .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, | ||
657 | .probe = fintek_probe, | ||
658 | .remove = __devexit_p(fintek_remove), | ||
659 | .suspend = fintek_suspend, | ||
660 | .resume = fintek_resume, | ||
661 | .shutdown = fintek_shutdown, | ||
662 | }; | ||
663 | |||
664 | int fintek_init(void) | ||
665 | { | ||
666 | return pnp_register_driver(&fintek_driver); | ||
667 | } | ||
668 | |||
669 | void fintek_exit(void) | ||
670 | { | ||
671 | pnp_unregister_driver(&fintek_driver); | ||
672 | } | ||
673 | |||
674 | module_param(debug, int, S_IRUGO | S_IWUSR); | ||
675 | MODULE_PARM_DESC(debug, "Enable debugging output"); | ||
676 | |||
677 | MODULE_DEVICE_TABLE(pnp, fintek_ids); | ||
678 | MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver"); | ||
679 | |||
680 | MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); | ||
681 | MODULE_LICENSE("GPL"); | ||
682 | |||
683 | module_init(fintek_init); | ||
684 | module_exit(fintek_exit); | ||
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h new file mode 100644 index 000000000000..1b10b2011f5e --- /dev/null +++ b/drivers/media/rc/fintek-cir.h | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR | ||
3 | * | ||
4 | * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> | ||
5 | * | ||
6 | * Special thanks to Fintek for providing hardware and spec sheets. | ||
7 | * This driver is based upon the nuvoton, ite and ene drivers for | ||
8 | * similar hardware. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation; either version 2 of the | ||
13 | * License, or (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
23 | * USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/ioctl.h> | ||
28 | |||
29 | /* platform driver name to register */ | ||
30 | #define FINTEK_DRIVER_NAME "fintek-cir" | ||
31 | #define FINTEK_DESCRIPTION "Fintek LPC SuperIO Consumer IR Transceiver" | ||
32 | #define VENDOR_ID_FINTEK 0x1934 | ||
33 | |||
34 | |||
35 | /* debugging module parameter */ | ||
36 | static int debug; | ||
37 | |||
38 | #define fit_pr(level, text, ...) \ | ||
39 | printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) | ||
40 | |||
41 | #define fit_dbg(text, ...) \ | ||
42 | if (debug) \ | ||
43 | printk(KERN_DEBUG \ | ||
44 | KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) | ||
45 | |||
46 | #define fit_dbg_verbose(text, ...) \ | ||
47 | if (debug > 1) \ | ||
48 | printk(KERN_DEBUG \ | ||
49 | KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) | ||
50 | |||
51 | #define fit_dbg_wake(text, ...) \ | ||
52 | if (debug > 2) \ | ||
53 | printk(KERN_DEBUG \ | ||
54 | KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) | ||
55 | |||
56 | |||
57 | #define TX_BUF_LEN 256 | ||
58 | #define RX_BUF_LEN 32 | ||
59 | |||
60 | struct fintek_dev { | ||
61 | struct pnp_dev *pdev; | ||
62 | struct rc_dev *rdev; | ||
63 | |||
64 | spinlock_t fintek_lock; | ||
65 | |||
66 | /* for rx */ | ||
67 | u8 buf[RX_BUF_LEN]; | ||
68 | unsigned int pkts; | ||
69 | |||
70 | struct { | ||
71 | spinlock_t lock; | ||
72 | u8 buf[TX_BUF_LEN]; | ||
73 | unsigned int buf_count; | ||
74 | unsigned int cur_buf_num; | ||
75 | wait_queue_head_t queue; | ||
76 | } tx; | ||
77 | |||
78 | /* Config register index/data port pair */ | ||
79 | u8 cr_ip; | ||
80 | u8 cr_dp; | ||
81 | |||
82 | /* hardware I/O settings */ | ||
83 | unsigned long cir_addr; | ||
84 | int cir_irq; | ||
85 | int cir_port_len; | ||
86 | |||
87 | /* hardware id */ | ||
88 | u8 chip_major; | ||
89 | u8 chip_minor; | ||
90 | u16 chip_vendor; | ||
91 | |||
92 | /* hardware features */ | ||
93 | bool hw_learning_capable; | ||
94 | bool hw_tx_capable; | ||
95 | |||
96 | /* rx settings */ | ||
97 | bool learning_enabled; | ||
98 | bool carrier_detect_enabled; | ||
99 | |||
100 | enum { | ||
101 | CMD_HEADER = 0, | ||
102 | SUBCMD, | ||
103 | CMD_DATA, | ||
104 | PARSE_IRDATA, | ||
105 | } parser_state; | ||
106 | |||
107 | u8 cmd, rem; | ||
108 | |||
109 | /* carrier period = 1 / frequency */ | ||
110 | u32 carrier; | ||
111 | }; | ||
112 | |||
113 | /* buffer packet constants, largely identical to mceusb.c */ | ||
114 | #define BUF_PULSE_BIT 0x80 | ||
115 | #define BUF_LEN_MASK 0x1f | ||
116 | #define BUF_SAMPLE_MASK 0x7f | ||
117 | |||
118 | #define BUF_COMMAND_HEADER 0x9f | ||
119 | #define BUF_COMMAND_MASK 0xe0 | ||
120 | #define BUF_COMMAND_NULL 0x00 | ||
121 | #define BUF_HW_CMD_HEADER 0xff | ||
122 | #define BUF_CMD_G_REVISION 0x0b | ||
123 | #define BUF_CMD_S_CARRIER 0x06 | ||
124 | #define BUF_CMD_S_TIMEOUT 0x0c | ||
125 | #define BUF_CMD_SIG_END 0x01 | ||
126 | #define BUF_CMD_S_TXMASK 0x08 | ||
127 | #define BUF_CMD_S_RXSENSOR 0x14 | ||
128 | #define BUF_RSP_PULSE_COUNT 0x15 | ||
129 | |||
130 | #define CIR_SAMPLE_PERIOD 50 | ||
131 | |||
132 | /* | ||
133 | * Configuration Register: | ||
134 | * Index Port | ||
135 | * Data Port | ||
136 | */ | ||
137 | #define CR_INDEX_PORT 0x2e | ||
138 | #define CR_DATA_PORT 0x2f | ||
139 | |||
140 | /* Possible alternate values, depends on how the chip is wired */ | ||
141 | #define CR_INDEX_PORT2 0x4e | ||
142 | #define CR_DATA_PORT2 0x4f | ||
143 | |||
144 | /* | ||
145 | * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is | ||
146 | * active. 1 = 0x4e, 0 = 0x2e | ||
147 | */ | ||
148 | #define PORT_SEL_PORT_4E_EN 0x10 | ||
149 | |||
150 | /* Extended Function Mode enable/disable magic values */ | ||
151 | #define CONFIG_REG_ENABLE 0x87 | ||
152 | #define CONFIG_REG_DISABLE 0xaa | ||
153 | |||
154 | /* Chip IDs found in CR_CHIP_ID_{HI,LO} */ | ||
155 | #define CHIP_ID_HIGH_F71809U 0x04 | ||
156 | #define CHIP_ID_LOW_F71809U 0x08 | ||
157 | |||
158 | /* | ||
159 | * Global control regs we need to care about: | ||
160 | * Global Control def. | ||
161 | * Register name addr val. */ | ||
162 | #define GCR_SOFTWARE_RESET 0x02 /* 0x00 */ | ||
163 | #define GCR_LOGICAL_DEV_NO 0x07 /* 0x00 */ | ||
164 | #define GCR_CHIP_ID_HI 0x20 /* 0x04 */ | ||
165 | #define GCR_CHIP_ID_LO 0x21 /* 0x08 */ | ||
166 | #define GCR_VENDOR_ID_HI 0x23 /* 0x19 */ | ||
167 | #define GCR_VENDOR_ID_LO 0x24 /* 0x34 */ | ||
168 | #define GCR_CONFIG_PORT_SEL 0x25 /* 0x01 */ | ||
169 | #define GCR_KBMOUSE_WAKEUP 0x27 | ||
170 | |||
171 | #define LOGICAL_DEV_DISABLE 0x00 | ||
172 | #define LOGICAL_DEV_ENABLE 0x01 | ||
173 | |||
174 | /* Logical device number of the CIR function */ | ||
175 | #define LOGICAL_DEV_CIR 0x05 | ||
176 | |||
177 | /* CIR Logical Device (LDN 0x08) config registers */ | ||
178 | #define CIR_CR_COMMAND_INDEX 0x04 | ||
179 | #define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host | ||
180 | must set to 1. When host finshes write | ||
181 | command to IR, host must clear to 0. */ | ||
182 | #define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */ | ||
183 | #define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx, | ||
184 | 0x33 = rx + 1 tx */ | ||
185 | #define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */ | ||
186 | #define CIR_CR_BASE_ADDR_HI 0x60 /* MSB of CIR IO base addr */ | ||
187 | #define CIR_CR_BASE_ADDR_LO 0x61 /* LSB of CIR IO base addr */ | ||
188 | #define CIR_CR_IRQ_SEL 0x70 /* bits3-0 store CIR IRQ */ | ||
189 | #define CIR_CR_PSOUT_STATUS 0xf1 | ||
190 | #define CIR_CR_WAKE_KEY3_ADDR 0xf8 | ||
191 | #define CIR_CR_WAKE_KEY3_CODE 0xf9 | ||
192 | #define CIR_CR_WAKE_KEY3_DC 0xfa | ||
193 | #define CIR_CR_WAKE_CONTROL 0xfb | ||
194 | #define CIR_CR_WAKE_KEY12_ADDR 0xfc | ||
195 | #define CIR_CR_WAKE_KEY4_ADDR 0xfd | ||
196 | #define CIR_CR_WAKE_KEY5_ADDR 0xfe | ||
197 | |||
198 | #define CLASS_RX_ONLY 0xff | ||
199 | #define CLASS_RX_2TX 0x66 | ||
200 | #define CLASS_RX_1TX 0x33 | ||
201 | |||
202 | /* CIR device registers */ | ||
203 | #define CIR_STATUS 0x00 | ||
204 | #define CIR_RX_DATA 0x01 | ||
205 | #define CIR_TX_CONTROL 0x02 | ||
206 | #define CIR_TX_DATA 0x03 | ||
207 | #define CIR_CONTROL 0x04 | ||
208 | |||
209 | /* Bits to enable CIR wake */ | ||
210 | #define LOGICAL_DEV_ACPI 0x01 | ||
211 | #define LDEV_ACPI_WAKE_EN_REG 0xe8 | ||
212 | #define ACPI_WAKE_EN_CIR_BIT 0x04 | ||
213 | |||
214 | #define LDEV_ACPI_PME_EN_REG 0xf0 | ||
215 | #define LDEV_ACPI_PME_CLR_REG 0xf1 | ||
216 | #define ACPI_PME_CIR_BIT 0x02 | ||
217 | |||
218 | #define LDEV_ACPI_STATE_REG 0xf4 | ||
219 | #define ACPI_STATE_CIR_BIT 0x20 | ||
220 | |||
221 | /* | ||
222 | * CIR status register (0x00): | ||
223 | * 7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable) | ||
224 | * 3 - TX_FINISH (1 when TX finished, write 1 to clear) | ||
225 | * 2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear) | ||
226 | * 1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear) | ||
227 | * 0 - RX_RECEIVE (1 on RX receive, write 1 to clear) | ||
228 | */ | ||
229 | #define CIR_STATUS_IRQ_EN 0x80 | ||
230 | #define CIR_STATUS_TX_FINISH 0x08 | ||
231 | #define CIR_STATUS_TX_UNDERRUN 0x04 | ||
232 | #define CIR_STATUS_RX_TIMEOUT 0x02 | ||
233 | #define CIR_STATUS_RX_RECEIVE 0x01 | ||
234 | #define CIR_STATUS_IRQ_MASK 0x0f | ||
235 | |||
236 | /* | ||
237 | * CIR TX control register (0x02): | ||
238 | * 7 - TX_START (1 to indicate TX start, auto-cleared when done) | ||
239 | * 6 - TX_END (1 to indicate TX data written to TX fifo) | ||
240 | */ | ||
241 | #define CIR_TX_CONTROL_TX_START 0x80 | ||
242 | #define CIR_TX_CONTROL_TX_END 0x40 | ||
243 | |||
diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c index afae14fd152e..129d3f9a461d 100644 --- a/drivers/media/rc/keymaps/rc-lme2510.c +++ b/drivers/media/rc/keymaps/rc-lme2510.c | |||
@@ -14,81 +14,81 @@ | |||
14 | 14 | ||
15 | static struct rc_map_table lme2510_rc[] = { | 15 | static struct rc_map_table lme2510_rc[] = { |
16 | /* Type 1 - 26 buttons */ | 16 | /* Type 1 - 26 buttons */ |
17 | { 0xef12ba45, KEY_0 }, | 17 | { 0x10ed45, KEY_0 }, |
18 | { 0xef12a05f, KEY_1 }, | 18 | { 0x10ed5f, KEY_1 }, |
19 | { 0xef12af50, KEY_2 }, | 19 | { 0x10ed50, KEY_2 }, |
20 | { 0xef12a25d, KEY_3 }, | 20 | { 0x10ed5d, KEY_3 }, |
21 | { 0xef12be41, KEY_4 }, | 21 | { 0x10ed41, KEY_4 }, |
22 | { 0xef12f50a, KEY_5 }, | 22 | { 0x10ed0a, KEY_5 }, |
23 | { 0xef12bd42, KEY_6 }, | 23 | { 0x10ed42, KEY_6 }, |
24 | { 0xef12b847, KEY_7 }, | 24 | { 0x10ed47, KEY_7 }, |
25 | { 0xef12b649, KEY_8 }, | 25 | { 0x10ed49, KEY_8 }, |
26 | { 0xef12fa05, KEY_9 }, | 26 | { 0x10ed05, KEY_9 }, |
27 | { 0xef12bc43, KEY_POWER }, | 27 | { 0x10ed43, KEY_POWER }, |
28 | { 0xef12b946, KEY_SUBTITLE }, | 28 | { 0x10ed46, KEY_SUBTITLE }, |
29 | { 0xef12f906, KEY_PAUSE }, | 29 | { 0x10ed06, KEY_PAUSE }, |
30 | { 0xef12fc03, KEY_MEDIA_REPEAT}, | 30 | { 0x10ed03, KEY_MEDIA_REPEAT}, |
31 | { 0xef12fd02, KEY_PAUSE }, | 31 | { 0x10ed02, KEY_PAUSE }, |
32 | { 0xef12a15e, KEY_VOLUMEUP }, | 32 | { 0x10ed5e, KEY_VOLUMEUP }, |
33 | { 0xef12a35c, KEY_VOLUMEDOWN }, | 33 | { 0x10ed5c, KEY_VOLUMEDOWN }, |
34 | { 0xef12f609, KEY_CHANNELUP }, | 34 | { 0x10ed09, KEY_CHANNELUP }, |
35 | { 0xef12e51a, KEY_CHANNELDOWN }, | 35 | { 0x10ed1a, KEY_CHANNELDOWN }, |
36 | { 0xef12e11e, KEY_PLAY }, | 36 | { 0x10ed1e, KEY_PLAY }, |
37 | { 0xef12e41b, KEY_ZOOM }, | 37 | { 0x10ed1b, KEY_ZOOM }, |
38 | { 0xef12a659, KEY_MUTE }, | 38 | { 0x10ed59, KEY_MUTE }, |
39 | { 0xef12a55a, KEY_TV }, | 39 | { 0x10ed5a, KEY_TV }, |
40 | { 0xef12e718, KEY_RECORD }, | 40 | { 0x10ed18, KEY_RECORD }, |
41 | { 0xef12f807, KEY_EPG }, | 41 | { 0x10ed07, KEY_EPG }, |
42 | { 0xef12fe01, KEY_STOP }, | 42 | { 0x10ed01, KEY_STOP }, |
43 | /* Type 2 - 20 buttons */ | 43 | /* Type 2 - 20 buttons */ |
44 | { 0xff40ea15, KEY_0 }, | 44 | { 0xbf15, KEY_0 }, |
45 | { 0xff40f708, KEY_1 }, | 45 | { 0xbf08, KEY_1 }, |
46 | { 0xff40f609, KEY_2 }, | 46 | { 0xbf09, KEY_2 }, |
47 | { 0xff40f50a, KEY_3 }, | 47 | { 0xbf0a, KEY_3 }, |
48 | { 0xff40f30c, KEY_4 }, | 48 | { 0xbf0c, KEY_4 }, |
49 | { 0xff40f20d, KEY_5 }, | 49 | { 0xbf0d, KEY_5 }, |
50 | { 0xff40f10e, KEY_6 }, | 50 | { 0xbf0e, KEY_6 }, |
51 | { 0xff40ef10, KEY_7 }, | 51 | { 0xbf10, KEY_7 }, |
52 | { 0xff40ee11, KEY_8 }, | 52 | { 0xbf11, KEY_8 }, |
53 | { 0xff40ed12, KEY_9 }, | 53 | { 0xbf12, KEY_9 }, |
54 | { 0xff40ff00, KEY_POWER }, | 54 | { 0xbf00, KEY_POWER }, |
55 | { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ | 55 | { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */ |
56 | { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ | 56 | { 0xbf1a, KEY_PAUSE }, /* Timeshift */ |
57 | { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ | 57 | { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ |
58 | { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ | 58 | { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ |
59 | { 0xff40fe01, KEY_CHANNELUP }, | 59 | { 0xbf01, KEY_CHANNELUP }, |
60 | { 0xff40fa05, KEY_CHANNELDOWN }, | 60 | { 0xbf05, KEY_CHANNELDOWN }, |
61 | { 0xff40eb14, KEY_ZOOM }, | 61 | { 0xbf14, KEY_ZOOM }, |
62 | { 0xff40e718, KEY_RECORD }, | 62 | { 0xbf18, KEY_RECORD }, |
63 | { 0xff40e916, KEY_STOP }, | 63 | { 0xbf16, KEY_STOP }, |
64 | /* Type 3 - 20 buttons */ | 64 | /* Type 3 - 20 buttons */ |
65 | { 0xff00e31c, KEY_0 }, | 65 | { 0x1c, KEY_0 }, |
66 | { 0xff00f807, KEY_1 }, | 66 | { 0x07, KEY_1 }, |
67 | { 0xff00ea15, KEY_2 }, | 67 | { 0x15, KEY_2 }, |
68 | { 0xff00f609, KEY_3 }, | 68 | { 0x09, KEY_3 }, |
69 | { 0xff00e916, KEY_4 }, | 69 | { 0x16, KEY_4 }, |
70 | { 0xff00e619, KEY_5 }, | 70 | { 0x19, KEY_5 }, |
71 | { 0xff00f20d, KEY_6 }, | 71 | { 0x0d, KEY_6 }, |
72 | { 0xff00f30c, KEY_7 }, | 72 | { 0x0c, KEY_7 }, |
73 | { 0xff00e718, KEY_8 }, | 73 | { 0x18, KEY_8 }, |
74 | { 0xff00a15e, KEY_9 }, | 74 | { 0x5e, KEY_9 }, |
75 | { 0xff00ba45, KEY_POWER }, | 75 | { 0x45, KEY_POWER }, |
76 | { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ | 76 | { 0x44, KEY_MEDIA_REPEAT}, /* Recall */ |
77 | { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ | 77 | { 0x4a, KEY_PAUSE }, /* Timeshift */ |
78 | { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ | 78 | { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ |
79 | { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ | 79 | { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ |
80 | { 0xff00b946, KEY_CHANNELUP }, | 80 | { 0x46, KEY_CHANNELUP }, |
81 | { 0xff00bf40, KEY_CHANNELDOWN }, | 81 | { 0x40, KEY_CHANNELDOWN }, |
82 | { 0xff00f708, KEY_ZOOM }, | 82 | { 0x08, KEY_ZOOM }, |
83 | { 0xff00bd42, KEY_RECORD }, | 83 | { 0x42, KEY_RECORD }, |
84 | { 0xff00a55a, KEY_STOP }, | 84 | { 0x5a, KEY_STOP }, |
85 | }; | 85 | }; |
86 | 86 | ||
87 | static struct rc_map_list lme2510_map = { | 87 | static struct rc_map_list lme2510_map = { |
88 | .map = { | 88 | .map = { |
89 | .scan = lme2510_rc, | 89 | .scan = lme2510_rc, |
90 | .size = ARRAY_SIZE(lme2510_rc), | 90 | .size = ARRAY_SIZE(lme2510_rc), |
91 | .rc_type = RC_TYPE_UNKNOWN, | 91 | .rc_type = RC_TYPE_NEC, |
92 | .name = RC_MAP_LME2510, | 92 | .name = RC_MAP_LME2510, |
93 | } | 93 | } |
94 | }; | 94 | }; |
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3be180b3ba27..bb53de7fe408 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -687,7 +687,7 @@ config VIDEO_HEXIUM_GEMINI | |||
687 | 687 | ||
688 | config VIDEO_TIMBERDALE | 688 | config VIDEO_TIMBERDALE |
689 | tristate "Support for timberdale Video In/LogiWIN" | 689 | tristate "Support for timberdale Video In/LogiWIN" |
690 | depends on VIDEO_V4L2 && I2C | 690 | depends on VIDEO_V4L2 && I2C && DMADEVICES |
691 | select DMA_ENGINE | 691 | select DMA_ENGINE |
692 | select TIMB_DMA | 692 | select TIMB_DMA |
693 | select VIDEO_ADV7180 | 693 | select VIDEO_ADV7180 |
@@ -757,6 +757,8 @@ config VIDEO_NOON010PC30 | |||
757 | ---help--- | 757 | ---help--- |
758 | This driver supports NOON010PC30 CIF camera from Siliconfile | 758 | This driver supports NOON010PC30 CIF camera from Siliconfile |
759 | 759 | ||
760 | source "drivers/media/video/m5mols/Kconfig" | ||
761 | |||
760 | config VIDEO_OMAP3 | 762 | config VIDEO_OMAP3 |
761 | tristate "OMAP 3 Camera support (EXPERIMENTAL)" | 763 | tristate "OMAP 3 Camera support (EXPERIMENTAL)" |
762 | select OMAP_IOMMU | 764 | select OMAP_IOMMU |
@@ -952,7 +954,7 @@ config VIDEO_SAMSUNG_S5P_FIMC | |||
952 | 954 | ||
953 | config VIDEO_S5P_MIPI_CSIS | 955 | config VIDEO_S5P_MIPI_CSIS |
954 | tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" | 956 | tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" |
955 | depends on VIDEO_V4L2 && PM_RUNTIME && VIDEO_V4L2_SUBDEV_API | 957 | depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API |
956 | ---help--- | 958 | ---help--- |
957 | This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. | 959 | This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. |
958 | 960 | ||
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9519160c2e01..f0fecd6f6a33 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o | |||
69 | obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o | 69 | obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o |
70 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o | 70 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o |
71 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o | 71 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o |
72 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ | ||
72 | 73 | ||
73 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o | 74 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o |
74 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o | 75 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o |
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 0073a8c55336..40eb6326e48a 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c | |||
@@ -438,7 +438,7 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v | |||
438 | strcat(vc->card, " (676/"); | 438 | strcat(vc->card, " (676/"); |
439 | break; | 439 | break; |
440 | default: | 440 | default: |
441 | strcat(vc->card, " (???/"); | 441 | strcat(vc->card, " (XXX/"); |
442 | break; | 442 | break; |
443 | } | 443 | } |
444 | switch (cam->params.version.sensor_flags) { | 444 | switch (cam->params.version.sensor_flags) { |
@@ -458,7 +458,7 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v | |||
458 | strcat(vc->card, "500)"); | 458 | strcat(vc->card, "500)"); |
459 | break; | 459 | break; |
460 | default: | 460 | default: |
461 | strcat(vc->card, "???)"); | 461 | strcat(vc->card, "XXX)"); |
462 | break; | 462 | break; |
463 | } | 463 | } |
464 | 464 | ||
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c index 66671a4092e4..26fc206f095e 100644 --- a/drivers/media/video/gspca/kinect.c +++ b/drivers/media/video/gspca/kinect.c | |||
@@ -34,7 +34,7 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | |||
34 | MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); | 34 | MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); |
35 | MODULE_LICENSE("GPL"); | 35 | MODULE_LICENSE("GPL"); |
36 | 36 | ||
37 | #ifdef DEBUG | 37 | #ifdef GSPCA_DEBUG |
38 | int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK | | 38 | int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK | |
39 | D_USBI | D_USBO | D_V4L2; | 39 | D_USBI | D_USBO | D_V4L2; |
40 | #endif | 40 | #endif |
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig new file mode 100644 index 000000000000..302dc3d70193 --- /dev/null +++ b/drivers/media/video/m5mols/Kconfig | |||
@@ -0,0 +1,5 @@ | |||
1 | config VIDEO_M5MOLS | ||
2 | tristate "Fujitsu M-5MOLS 8MP sensor support" | ||
3 | depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API | ||
4 | ---help--- | ||
5 | This driver supports Fujitsu M-5MOLS camera sensor with ISP | ||
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile new file mode 100644 index 000000000000..0a44e028edc7 --- /dev/null +++ b/drivers/media/video/m5mols/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o | ||
2 | |||
3 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o | ||
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h new file mode 100644 index 000000000000..10b55c854487 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols.h | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | * Header for M-5MOLS 8M Pixel camera sensor with ISP | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * Author: HeungJun Kim, riverful.kim@samsung.com | ||
6 | * | ||
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
8 | * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #ifndef M5MOLS_H | ||
17 | #define M5MOLS_H | ||
18 | |||
19 | #include <media/v4l2-subdev.h> | ||
20 | #include "m5mols_reg.h" | ||
21 | |||
22 | extern int m5mols_debug; | ||
23 | |||
24 | #define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd) | ||
25 | |||
26 | #define to_sd(__ctrl) \ | ||
27 | (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd) | ||
28 | |||
29 | enum m5mols_restype { | ||
30 | M5MOLS_RESTYPE_MONITOR, | ||
31 | M5MOLS_RESTYPE_CAPTURE, | ||
32 | M5MOLS_RESTYPE_MAX, | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * struct m5mols_resolution - structure for the resolution | ||
37 | * @type: resolution type according to the pixel code | ||
38 | * @width: width of the resolution | ||
39 | * @height: height of the resolution | ||
40 | * @reg: resolution preset register value | ||
41 | */ | ||
42 | struct m5mols_resolution { | ||
43 | u8 reg; | ||
44 | enum m5mols_restype type; | ||
45 | u16 width; | ||
46 | u16 height; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct m5mols_exif - structure for the EXIF information of M-5MOLS | ||
51 | * @exposure_time: exposure time register value | ||
52 | * @shutter_speed: speed of the shutter register value | ||
53 | * @aperture: aperture register value | ||
54 | * @exposure_bias: it calls also EV bias | ||
55 | * @iso_speed: ISO register value | ||
56 | * @flash: status register value of the flash | ||
57 | * @sdr: status register value of the Subject Distance Range | ||
58 | * @qval: not written exact meaning in document | ||
59 | */ | ||
60 | struct m5mols_exif { | ||
61 | u32 exposure_time; | ||
62 | u32 shutter_speed; | ||
63 | u32 aperture; | ||
64 | u32 brightness; | ||
65 | u32 exposure_bias; | ||
66 | u16 iso_speed; | ||
67 | u16 flash; | ||
68 | u16 sdr; | ||
69 | u16 qval; | ||
70 | }; | ||
71 | |||
72 | /** | ||
73 | * struct m5mols_capture - Structure for the capture capability | ||
74 | * @exif: EXIF information | ||
75 | * @main: size in bytes of the main image | ||
76 | * @thumb: size in bytes of the thumb image, if it was accompanied | ||
77 | * @total: total size in bytes of the produced image | ||
78 | */ | ||
79 | struct m5mols_capture { | ||
80 | struct m5mols_exif exif; | ||
81 | u32 main; | ||
82 | u32 thumb; | ||
83 | u32 total; | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * struct m5mols_scenemode - structure for the scenemode capability | ||
88 | * @metering: metering light register value | ||
89 | * @ev_bias: EV bias register value | ||
90 | * @wb_mode: mode which means the WhiteBalance is Auto or Manual | ||
91 | * @wb_preset: whitebalance preset register value in the Manual mode | ||
92 | * @chroma_en: register value whether the Chroma capability is enabled or not | ||
93 | * @chroma_lvl: chroma's level register value | ||
94 | * @edge_en: register value Whether the Edge capability is enabled or not | ||
95 | * @edge_lvl: edge's level register value | ||
96 | * @af_range: Auto Focus's range | ||
97 | * @fd_mode: Face Detection mode | ||
98 | * @mcc: Multi-axis Color Conversion which means emotion color | ||
99 | * @light: status of the Light | ||
100 | * @flash: status of the Flash | ||
101 | * @tone: Tone color which means Contrast | ||
102 | * @iso: ISO register value | ||
103 | * @capt_mode: Mode of the Image Stabilization while the camera capturing | ||
104 | * @wdr: Wide Dynamic Range register value | ||
105 | * | ||
106 | * The each value according to each scenemode is recommended in the documents. | ||
107 | */ | ||
108 | struct m5mols_scenemode { | ||
109 | u32 metering; | ||
110 | u32 ev_bias; | ||
111 | u32 wb_mode; | ||
112 | u32 wb_preset; | ||
113 | u32 chroma_en; | ||
114 | u32 chroma_lvl; | ||
115 | u32 edge_en; | ||
116 | u32 edge_lvl; | ||
117 | u32 af_range; | ||
118 | u32 fd_mode; | ||
119 | u32 mcc; | ||
120 | u32 light; | ||
121 | u32 flash; | ||
122 | u32 tone; | ||
123 | u32 iso; | ||
124 | u32 capt_mode; | ||
125 | u32 wdr; | ||
126 | }; | ||
127 | |||
128 | /** | ||
129 | * struct m5mols_version - firmware version information | ||
130 | * @customer: customer information | ||
131 | * @project: version of project information according to customer | ||
132 | * @fw: firmware revision | ||
133 | * @hw: hardware revision | ||
134 | * @param: version of the parameter | ||
135 | * @awb: Auto WhiteBalance algorithm version | ||
136 | * @str: information about manufacturer and packaging vendor | ||
137 | * @af: Auto Focus version | ||
138 | * | ||
139 | * The register offset starts the customer version at 0x0, and it ends | ||
140 | * the awb version at 0x09. The customer, project information occupies 1 bytes | ||
141 | * each. And also the fw, hw, param, awb each requires 2 bytes. The str is | ||
142 | * unique string associated with firmware's version. It includes information | ||
143 | * about manufacturer and the vendor of the sensor's packaging. The least | ||
144 | * significant 2 bytes of the string indicate packaging manufacturer. | ||
145 | */ | ||
146 | #define VERSION_STRING_SIZE 22 | ||
147 | struct m5mols_version { | ||
148 | u8 customer; | ||
149 | u8 project; | ||
150 | u16 fw; | ||
151 | u16 hw; | ||
152 | u16 param; | ||
153 | u16 awb; | ||
154 | u8 str[VERSION_STRING_SIZE]; | ||
155 | u8 af; | ||
156 | }; | ||
157 | #define VERSION_SIZE sizeof(struct m5mols_version) | ||
158 | |||
159 | /** | ||
160 | * struct m5mols_info - M-5MOLS driver data structure | ||
161 | * @pdata: platform data | ||
162 | * @sd: v4l-subdev instance | ||
163 | * @pad: media pad | ||
164 | * @ffmt: current fmt according to resolution type | ||
165 | * @res_type: current resolution type | ||
166 | * @code: current code | ||
167 | * @irq_waitq: waitqueue for the capture | ||
168 | * @work_irq: workqueue for the IRQ | ||
169 | * @flags: state variable for the interrupt handler | ||
170 | * @handle: control handler | ||
171 | * @autoexposure: Auto Exposure control | ||
172 | * @exposure: Exposure control | ||
173 | * @autowb: Auto White Balance control | ||
174 | * @colorfx: Color effect control | ||
175 | * @saturation: Saturation control | ||
176 | * @zoom: Zoom control | ||
177 | * @ver: information of the version | ||
178 | * @cap: the capture mode attributes | ||
179 | * @power: current sensor's power status | ||
180 | * @ctrl_sync: true means all controls of the sensor are initialized | ||
181 | * @int_capture: true means the capture interrupt is issued once | ||
182 | * @lock_ae: true means the Auto Exposure is locked | ||
183 | * @lock_awb: true means the Aut WhiteBalance is locked | ||
184 | * @resolution: register value for current resolution | ||
185 | * @interrupt: register value for current interrupt status | ||
186 | * @mode: register value for current operation mode | ||
187 | * @mode_save: register value for current operation mode for saving | ||
188 | * @set_power: optional power callback to the board code | ||
189 | */ | ||
190 | struct m5mols_info { | ||
191 | const struct m5mols_platform_data *pdata; | ||
192 | struct v4l2_subdev sd; | ||
193 | struct media_pad pad; | ||
194 | struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; | ||
195 | int res_type; | ||
196 | enum v4l2_mbus_pixelcode code; | ||
197 | wait_queue_head_t irq_waitq; | ||
198 | struct work_struct work_irq; | ||
199 | unsigned long flags; | ||
200 | |||
201 | struct v4l2_ctrl_handler handle; | ||
202 | /* Autoexposure/exposure control cluster */ | ||
203 | struct { | ||
204 | struct v4l2_ctrl *autoexposure; | ||
205 | struct v4l2_ctrl *exposure; | ||
206 | }; | ||
207 | struct v4l2_ctrl *autowb; | ||
208 | struct v4l2_ctrl *colorfx; | ||
209 | struct v4l2_ctrl *saturation; | ||
210 | struct v4l2_ctrl *zoom; | ||
211 | |||
212 | struct m5mols_version ver; | ||
213 | struct m5mols_capture cap; | ||
214 | bool power; | ||
215 | bool ctrl_sync; | ||
216 | bool lock_ae; | ||
217 | bool lock_awb; | ||
218 | u8 resolution; | ||
219 | u32 interrupt; | ||
220 | u32 mode; | ||
221 | u32 mode_save; | ||
222 | int (*set_power)(struct device *dev, int on); | ||
223 | }; | ||
224 | |||
225 | #define ST_CAPT_IRQ 0 | ||
226 | |||
227 | #define is_powered(__info) (__info->power) | ||
228 | #define is_ctrl_synced(__info) (__info->ctrl_sync) | ||
229 | #define is_available_af(__info) (__info->ver.af) | ||
230 | #define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) | ||
231 | #define is_manufacturer(__info, __manufacturer) \ | ||
232 | (__info->ver.str[0] == __manufacturer[0] && \ | ||
233 | __info->ver.str[1] == __manufacturer[1]) | ||
234 | /* | ||
235 | * I2C operation of the M-5MOLS | ||
236 | * | ||
237 | * The I2C read operation of the M-5MOLS requires 2 messages. The first | ||
238 | * message sends the information about the command, command category, and total | ||
239 | * message size. The second message is used to retrieve the data specifed in | ||
240 | * the first message | ||
241 | * | ||
242 | * 1st message 2nd message | ||
243 | * +-------+---+----------+-----+-------+ +------+------+------+------+ | ||
244 | * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | | ||
245 | * +-------+---+----------+-----+-------+ +------+------+------+------+ | ||
246 | * - size1: message data size(5 in this case) | ||
247 | * - size2: desired buffer size of the 2nd message | ||
248 | * - d[0..3]: according to size2 | ||
249 | * | ||
250 | * The I2C write operation needs just one message. The message includes | ||
251 | * category, command, total size, and desired data. | ||
252 | * | ||
253 | * 1st message | ||
254 | * +-------+---+----------+-----+------+------+------+------+ | ||
255 | * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | | ||
256 | * +-------+---+----------+-----+------+------+------+------+ | ||
257 | * - d[0..3]: according to size1 | ||
258 | */ | ||
259 | int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); | ||
260 | int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); | ||
261 | int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value); | ||
262 | |||
263 | /* | ||
264 | * Mode operation of the M-5MOLS | ||
265 | * | ||
266 | * Changing the mode of the M-5MOLS is needed right executing order. | ||
267 | * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed | ||
268 | * by user. There are various categories associated with each mode. | ||
269 | * | ||
270 | * +============================================================+ | ||
271 | * | mode | category | | ||
272 | * +============================================================+ | ||
273 | * | FLASH | FLASH(only after Stand-by or Power-on) | | ||
274 | * | SYSTEM | SYSTEM(only after sensor arm-booting) | | ||
275 | * | PARAMETER | PARAMETER | | ||
276 | * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | | ||
277 | * | CAPTURE | Single CAPTURE, Preview(recording) | | ||
278 | * +============================================================+ | ||
279 | * | ||
280 | * The available executing order between each modes are as follows: | ||
281 | * PARAMETER <---> MONITOR <---> CAPTURE | ||
282 | */ | ||
283 | int m5mols_mode(struct m5mols_info *info, u32 mode); | ||
284 | |||
285 | int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg); | ||
286 | int m5mols_sync_controls(struct m5mols_info *info); | ||
287 | int m5mols_start_capture(struct m5mols_info *info); | ||
288 | int m5mols_do_scenemode(struct m5mols_info *info, u32 mode); | ||
289 | int m5mols_lock_3a(struct m5mols_info *info, bool lock); | ||
290 | int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); | ||
291 | |||
292 | /* The firmware function */ | ||
293 | int m5mols_update_fw(struct v4l2_subdev *sd, | ||
294 | int (*set_power)(struct m5mols_info *, bool)); | ||
295 | |||
296 | #endif /* M5MOLS_H */ | ||
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c new file mode 100644 index 000000000000..d71a3903b60f --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_capture.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * The Capture code for Fujitsu M-5MOLS ISP | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * Author: HeungJun Kim, riverful.kim@samsung.com | ||
6 | * | ||
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
8 | * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/version.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <linux/videodev2.h> | ||
25 | #include <linux/version.h> | ||
26 | #include <media/v4l2-ctrls.h> | ||
27 | #include <media/v4l2-device.h> | ||
28 | #include <media/v4l2-subdev.h> | ||
29 | #include <media/m5mols.h> | ||
30 | |||
31 | #include "m5mols.h" | ||
32 | #include "m5mols_reg.h" | ||
33 | |||
34 | static int m5mols_capture_error_handler(struct m5mols_info *info, | ||
35 | int timeout) | ||
36 | { | ||
37 | int ret; | ||
38 | |||
39 | /* Disable all interrupts and clear relevant interrupt staus bits */ | ||
40 | ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE, | ||
41 | info->interrupt & ~(REG_INT_CAPTURE)); | ||
42 | if (ret) | ||
43 | return ret; | ||
44 | |||
45 | if (timeout == 0) | ||
46 | return -ETIMEDOUT; | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | /** | ||
51 | * m5mols_read_rational - I2C read of a rational number | ||
52 | * | ||
53 | * Read numerator and denominator from registers @addr_num and @addr_den | ||
54 | * respectively and return the division result in @val. | ||
55 | */ | ||
56 | static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, | ||
57 | u32 addr_den, u32 *val) | ||
58 | { | ||
59 | u32 num, den; | ||
60 | |||
61 | int ret = m5mols_read(sd, addr_num, &num); | ||
62 | if (!ret) | ||
63 | ret = m5mols_read(sd, addr_den, &den); | ||
64 | if (ret) | ||
65 | return ret; | ||
66 | *val = den == 0 ? 0 : num / den; | ||
67 | return ret; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * m5mols_capture_info - Gather captured image information | ||
72 | * | ||
73 | * For now it gathers only EXIF information and file size. | ||
74 | */ | ||
75 | static int m5mols_capture_info(struct m5mols_info *info) | ||
76 | { | ||
77 | struct m5mols_exif *exif = &info->cap.exif; | ||
78 | struct v4l2_subdev *sd = &info->sd; | ||
79 | int ret; | ||
80 | |||
81 | ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, | ||
82 | EXIF_INFO_EXPTIME_DE, &exif->exposure_time); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, | ||
86 | &exif->shutter_speed); | ||
87 | if (ret) | ||
88 | return ret; | ||
89 | ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, | ||
90 | &exif->aperture); | ||
91 | if (ret) | ||
92 | return ret; | ||
93 | ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, | ||
94 | &exif->brightness); | ||
95 | if (ret) | ||
96 | return ret; | ||
97 | ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, | ||
98 | &exif->exposure_bias); | ||
99 | if (ret) | ||
100 | return ret; | ||
101 | |||
102 | ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed); | ||
103 | if (!ret) | ||
104 | ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash); | ||
105 | if (!ret) | ||
106 | ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr); | ||
107 | if (!ret) | ||
108 | ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval); | ||
109 | if (ret) | ||
110 | return ret; | ||
111 | |||
112 | if (!ret) | ||
113 | ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main); | ||
114 | if (!ret) | ||
115 | ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb); | ||
116 | if (!ret) | ||
117 | info->cap.total = info->cap.main + info->cap.thumb; | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | int m5mols_start_capture(struct m5mols_info *info) | ||
123 | { | ||
124 | struct v4l2_subdev *sd = &info->sd; | ||
125 | u32 resolution = info->resolution; | ||
126 | int timeout; | ||
127 | int ret; | ||
128 | |||
129 | /* | ||
130 | * Preparing capture. Setting control & interrupt before entering | ||
131 | * capture mode | ||
132 | * | ||
133 | * 1) change to MONITOR mode for operating control & interrupt | ||
134 | * 2) set controls (considering v4l2_control value & lock 3A) | ||
135 | * 3) set interrupt | ||
136 | * 4) change to CAPTURE mode | ||
137 | */ | ||
138 | ret = m5mols_mode(info, REG_MONITOR); | ||
139 | if (!ret) | ||
140 | ret = m5mols_sync_controls(info); | ||
141 | if (!ret) | ||
142 | ret = m5mols_lock_3a(info, true); | ||
143 | if (!ret) | ||
144 | ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); | ||
145 | if (!ret) | ||
146 | ret = m5mols_mode(info, REG_CAPTURE); | ||
147 | if (!ret) { | ||
148 | /* Wait for capture interrupt, after changing capture mode */ | ||
149 | timeout = wait_event_interruptible_timeout(info->irq_waitq, | ||
150 | test_bit(ST_CAPT_IRQ, &info->flags), | ||
151 | msecs_to_jiffies(2000)); | ||
152 | if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) | ||
153 | ret = m5mols_capture_error_handler(info, timeout); | ||
154 | } | ||
155 | if (!ret) | ||
156 | ret = m5mols_lock_3a(info, false); | ||
157 | if (ret) | ||
158 | return ret; | ||
159 | /* | ||
160 | * Starting capture. Setting capture frame count and resolution and | ||
161 | * the format(available format: JPEG, Bayer RAW, YUV). | ||
162 | * | ||
163 | * 1) select single or multi(enable to 25), format, size | ||
164 | * 2) set interrupt | ||
165 | * 3) start capture(for main image, now) | ||
166 | * 4) get information | ||
167 | * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device) | ||
168 | */ | ||
169 | ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); | ||
170 | if (!ret) | ||
171 | ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); | ||
172 | if (!ret) | ||
173 | ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); | ||
174 | if (!ret) | ||
175 | ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); | ||
176 | if (!ret) | ||
177 | ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); | ||
178 | if (!ret) { | ||
179 | /* Wait for the capture completion interrupt */ | ||
180 | timeout = wait_event_interruptible_timeout(info->irq_waitq, | ||
181 | test_bit(ST_CAPT_IRQ, &info->flags), | ||
182 | msecs_to_jiffies(2000)); | ||
183 | if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) { | ||
184 | ret = m5mols_capture_info(info); | ||
185 | if (!ret) | ||
186 | v4l2_subdev_notify(sd, 0, &info->cap.total); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | return m5mols_capture_error_handler(info, timeout); | ||
191 | } | ||
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c new file mode 100644 index 000000000000..817c16fec368 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_controls.c | |||
@@ -0,0 +1,299 @@ | |||
1 | /* | ||
2 | * Controls for M-5MOLS 8M Pixel camera sensor with ISP | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * Author: HeungJun Kim, riverful.kim@samsung.com | ||
6 | * | ||
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
8 | * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/videodev2.h> | ||
19 | #include <media/v4l2-ctrls.h> | ||
20 | |||
21 | #include "m5mols.h" | ||
22 | #include "m5mols_reg.h" | ||
23 | |||
24 | static struct m5mols_scenemode m5mols_default_scenemode[] = { | ||
25 | [REG_SCENE_NORMAL] = { | ||
26 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
27 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
28 | REG_AF_NORMAL, REG_FD_OFF, | ||
29 | REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
30 | 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
31 | }, | ||
32 | [REG_SCENE_PORTRAIT] = { | ||
33 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
34 | REG_CHROMA_ON, 3, REG_EDGE_ON, 4, | ||
35 | REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, | ||
36 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
37 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
38 | }, | ||
39 | [REG_SCENE_LANDSCAPE] = { | ||
40 | REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
41 | REG_CHROMA_ON, 4, REG_EDGE_ON, 6, | ||
42 | REG_AF_NORMAL, REG_FD_OFF, | ||
43 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
44 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
45 | }, | ||
46 | [REG_SCENE_SPORTS] = { | ||
47 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
48 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
49 | REG_AF_NORMAL, REG_FD_OFF, | ||
50 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
51 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
52 | }, | ||
53 | [REG_SCENE_PARTY_INDOOR] = { | ||
54 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
55 | REG_CHROMA_ON, 4, REG_EDGE_ON, 5, | ||
56 | REG_AF_NORMAL, REG_FD_OFF, | ||
57 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
58 | 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, | ||
59 | }, | ||
60 | [REG_SCENE_BEACH_SNOW] = { | ||
61 | REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, | ||
62 | REG_CHROMA_ON, 4, REG_EDGE_ON, 5, | ||
63 | REG_AF_NORMAL, REG_FD_OFF, | ||
64 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
65 | 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, | ||
66 | }, | ||
67 | [REG_SCENE_SUNSET] = { | ||
68 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, | ||
69 | REG_AWB_DAYLIGHT, | ||
70 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
71 | REG_AF_NORMAL, REG_FD_OFF, | ||
72 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
73 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
74 | }, | ||
75 | [REG_SCENE_DAWN_DUSK] = { | ||
76 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, | ||
77 | REG_AWB_FLUORESCENT_1, | ||
78 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
79 | REG_AF_NORMAL, REG_FD_OFF, | ||
80 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
81 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
82 | }, | ||
83 | [REG_SCENE_FALL] = { | ||
84 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
85 | REG_CHROMA_ON, 5, REG_EDGE_ON, 5, | ||
86 | REG_AF_NORMAL, REG_FD_OFF, | ||
87 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
88 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
89 | }, | ||
90 | [REG_SCENE_NIGHT] = { | ||
91 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
92 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
93 | REG_AF_NORMAL, REG_FD_OFF, | ||
94 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
95 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
96 | }, | ||
97 | [REG_SCENE_AGAINST_LIGHT] = { | ||
98 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
99 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
100 | REG_AF_NORMAL, REG_FD_OFF, | ||
101 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
102 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
103 | }, | ||
104 | [REG_SCENE_FIRE] = { | ||
105 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
106 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
107 | REG_AF_NORMAL, REG_FD_OFF, | ||
108 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
109 | 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, | ||
110 | }, | ||
111 | [REG_SCENE_TEXT] = { | ||
112 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
113 | REG_CHROMA_ON, 3, REG_EDGE_ON, 7, | ||
114 | REG_AF_MACRO, REG_FD_OFF, | ||
115 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
116 | 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, | ||
117 | }, | ||
118 | [REG_SCENE_CANDLE] = { | ||
119 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
120 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
121 | REG_AF_NORMAL, REG_FD_OFF, | ||
122 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
123 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
124 | }, | ||
125 | }; | ||
126 | |||
127 | /** | ||
128 | * m5mols_do_scenemode() - Change current scenemode | ||
129 | * @mode: Desired mode of the scenemode | ||
130 | * | ||
131 | * WARNING: The execution order is important. Do not change the order. | ||
132 | */ | ||
133 | int m5mols_do_scenemode(struct m5mols_info *info, u32 mode) | ||
134 | { | ||
135 | struct v4l2_subdev *sd = &info->sd; | ||
136 | struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; | ||
137 | int ret; | ||
138 | |||
139 | if (mode > REG_SCENE_CANDLE) | ||
140 | return -EINVAL; | ||
141 | |||
142 | ret = m5mols_lock_3a(info, false); | ||
143 | if (!ret) | ||
144 | ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); | ||
145 | if (!ret) | ||
146 | ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); | ||
147 | if (!ret) | ||
148 | ret = m5mols_write(sd, AE_MODE, scenemode.metering); | ||
149 | if (!ret) | ||
150 | ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); | ||
151 | if (!ret) | ||
152 | ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); | ||
153 | if (!ret) | ||
154 | ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); | ||
155 | if (!ret) | ||
156 | ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); | ||
157 | if (!ret) | ||
158 | ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); | ||
159 | if (!ret) | ||
160 | ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); | ||
161 | if (!ret) | ||
162 | ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); | ||
163 | if (!ret && is_available_af(info)) | ||
164 | ret = m5mols_write(sd, AF_MODE, scenemode.af_range); | ||
165 | if (!ret && is_available_af(info)) | ||
166 | ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); | ||
167 | if (!ret) | ||
168 | ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); | ||
169 | if (!ret) | ||
170 | ret = m5mols_write(sd, AE_ISO, scenemode.iso); | ||
171 | if (!ret) | ||
172 | ret = m5mols_mode(info, REG_CAPTURE); | ||
173 | if (!ret) | ||
174 | ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); | ||
175 | if (!ret) | ||
176 | ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); | ||
177 | if (!ret) | ||
178 | ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); | ||
179 | if (!ret) | ||
180 | ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); | ||
181 | if (!ret) | ||
182 | ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); | ||
183 | if (!ret) | ||
184 | ret = m5mols_mode(info, REG_MONITOR); | ||
185 | |||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static int m5mols_lock_ae(struct m5mols_info *info, bool lock) | ||
190 | { | ||
191 | int ret = 0; | ||
192 | |||
193 | if (info->lock_ae != lock) | ||
194 | ret = m5mols_write(&info->sd, AE_LOCK, | ||
195 | lock ? REG_AE_LOCK : REG_AE_UNLOCK); | ||
196 | if (!ret) | ||
197 | info->lock_ae = lock; | ||
198 | |||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | static int m5mols_lock_awb(struct m5mols_info *info, bool lock) | ||
203 | { | ||
204 | int ret = 0; | ||
205 | |||
206 | if (info->lock_awb != lock) | ||
207 | ret = m5mols_write(&info->sd, AWB_LOCK, | ||
208 | lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); | ||
209 | if (!ret) | ||
210 | info->lock_awb = lock; | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | /* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ | ||
216 | int m5mols_lock_3a(struct m5mols_info *info, bool lock) | ||
217 | { | ||
218 | int ret; | ||
219 | |||
220 | ret = m5mols_lock_ae(info, lock); | ||
221 | if (!ret) | ||
222 | ret = m5mols_lock_awb(info, lock); | ||
223 | /* Don't need to handle unlocking AF */ | ||
224 | if (!ret && is_available_af(info) && lock) | ||
225 | ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); | ||
226 | |||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | /* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ | ||
231 | int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) | ||
232 | { | ||
233 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
234 | struct m5mols_info *info = to_m5mols(sd); | ||
235 | int ret; | ||
236 | |||
237 | switch (ctrl->id) { | ||
238 | case V4L2_CID_ZOOM_ABSOLUTE: | ||
239 | return m5mols_write(sd, MON_ZOOM, ctrl->val); | ||
240 | |||
241 | case V4L2_CID_EXPOSURE_AUTO: | ||
242 | ret = m5mols_lock_ae(info, | ||
243 | ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); | ||
244 | if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) | ||
245 | ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); | ||
246 | if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { | ||
247 | int val = info->exposure->val; | ||
248 | ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); | ||
249 | if (!ret) | ||
250 | ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); | ||
251 | if (!ret) | ||
252 | ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); | ||
253 | } | ||
254 | return ret; | ||
255 | |||
256 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
257 | ret = m5mols_lock_awb(info, ctrl->val ? false : true); | ||
258 | if (!ret) | ||
259 | ret = m5mols_write(sd, AWB_MODE, ctrl->val ? | ||
260 | REG_AWB_AUTO : REG_AWB_PRESET); | ||
261 | return ret; | ||
262 | |||
263 | case V4L2_CID_SATURATION: | ||
264 | ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); | ||
265 | if (!ret) | ||
266 | ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); | ||
267 | return ret; | ||
268 | |||
269 | case V4L2_CID_COLORFX: | ||
270 | /* | ||
271 | * This control uses two kinds of registers: normal & color. | ||
272 | * The normal effect belongs to category 1, while the color | ||
273 | * one belongs to category 2. | ||
274 | * | ||
275 | * The normal effect uses one register: CAT1_EFFECT. | ||
276 | * The color effect uses three registers: | ||
277 | * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. | ||
278 | */ | ||
279 | ret = m5mols_write(sd, PARM_EFFECT, | ||
280 | ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : | ||
281 | ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : | ||
282 | REG_EFFECT_OFF); | ||
283 | if (!ret) | ||
284 | ret = m5mols_write(sd, MON_EFFECT, | ||
285 | ctrl->val == V4L2_COLORFX_SEPIA ? | ||
286 | REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); | ||
287 | if (!ret) | ||
288 | ret = m5mols_write(sd, MON_CFIXR, | ||
289 | ctrl->val == V4L2_COLORFX_SEPIA ? | ||
290 | REG_CFIXR_SEPIA : 0); | ||
291 | if (!ret) | ||
292 | ret = m5mols_write(sd, MON_CFIXB, | ||
293 | ctrl->val == V4L2_COLORFX_SEPIA ? | ||
294 | REG_CFIXB_SEPIA : 0); | ||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | return -EINVAL; | ||
299 | } | ||
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c new file mode 100644 index 000000000000..76eac26e84ae --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_core.c | |||
@@ -0,0 +1,1004 @@ | |||
1 | /* | ||
2 | * Driver for M-5MOLS 8M Pixel camera sensor with ISP | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * Author: HeungJun Kim, riverful.kim@samsung.com | ||
6 | * | ||
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
8 | * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/version.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <linux/videodev2.h> | ||
25 | #include <media/v4l2-ctrls.h> | ||
26 | #include <media/v4l2-device.h> | ||
27 | #include <media/v4l2-subdev.h> | ||
28 | #include <media/m5mols.h> | ||
29 | |||
30 | #include "m5mols.h" | ||
31 | #include "m5mols_reg.h" | ||
32 | |||
33 | int m5mols_debug; | ||
34 | module_param(m5mols_debug, int, 0644); | ||
35 | |||
36 | #define MODULE_NAME "M5MOLS" | ||
37 | #define M5MOLS_I2C_CHECK_RETRY 500 | ||
38 | |||
39 | /* The regulator consumer names for external voltage regulators */ | ||
40 | static struct regulator_bulk_data supplies[] = { | ||
41 | { | ||
42 | .supply = "core", /* ARM core power, 1.2V */ | ||
43 | }, { | ||
44 | .supply = "dig_18", /* digital power 1, 1.8V */ | ||
45 | }, { | ||
46 | .supply = "d_sensor", /* sensor power 1, 1.8V */ | ||
47 | }, { | ||
48 | .supply = "dig_28", /* digital power 2, 2.8V */ | ||
49 | }, { | ||
50 | .supply = "a_sensor", /* analog power */ | ||
51 | }, { | ||
52 | .supply = "dig_12", /* digital power 3, 1.2V */ | ||
53 | }, | ||
54 | }; | ||
55 | |||
56 | static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { | ||
57 | [M5MOLS_RESTYPE_MONITOR] = { | ||
58 | .width = 1920, | ||
59 | .height = 1080, | ||
60 | .code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
61 | .field = V4L2_FIELD_NONE, | ||
62 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
63 | }, | ||
64 | [M5MOLS_RESTYPE_CAPTURE] = { | ||
65 | .width = 1920, | ||
66 | .height = 1080, | ||
67 | .code = V4L2_MBUS_FMT_JPEG_1X8, | ||
68 | .field = V4L2_FIELD_NONE, | ||
69 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
70 | }, | ||
71 | }; | ||
72 | #define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) | ||
73 | |||
74 | static const struct m5mols_resolution m5mols_reg_res[] = { | ||
75 | { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ | ||
76 | { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ | ||
77 | { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ | ||
78 | { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, | ||
79 | { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ | ||
80 | { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ | ||
81 | { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ | ||
82 | { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ | ||
83 | { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ | ||
84 | { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, | ||
85 | { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ | ||
86 | { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ | ||
87 | { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, | ||
88 | { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ | ||
89 | { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ | ||
90 | { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ | ||
91 | { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ | ||
92 | { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ | ||
93 | { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ | ||
94 | |||
95 | { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ | ||
96 | { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ | ||
97 | { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, | ||
98 | { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ | ||
99 | { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ | ||
100 | { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ | ||
101 | { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ | ||
102 | { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ | ||
103 | { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ | ||
104 | { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ | ||
105 | { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ | ||
106 | { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, | ||
107 | { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ | ||
108 | { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, | ||
109 | { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ | ||
110 | { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ | ||
111 | { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, | ||
112 | { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ | ||
113 | }; | ||
114 | |||
115 | /** | ||
116 | * m5mols_swap_byte - an byte array to integer conversion function | ||
117 | * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet | ||
118 | * | ||
119 | * Convert I2C data byte array with performing any required byte | ||
120 | * reordering to assure proper values for each data type, regardless | ||
121 | * of the architecture endianness. | ||
122 | */ | ||
123 | static u32 m5mols_swap_byte(u8 *data, u8 length) | ||
124 | { | ||
125 | if (length == 1) | ||
126 | return *data; | ||
127 | else if (length == 2) | ||
128 | return be16_to_cpu(*((u16 *)data)); | ||
129 | else | ||
130 | return be32_to_cpu(*((u32 *)data)); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * m5mols_read - I2C read function | ||
135 | * @reg: combination of size, category and command for the I2C packet | ||
136 | * @val: read value | ||
137 | */ | ||
138 | int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val) | ||
139 | { | ||
140 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
141 | u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; | ||
142 | u8 size = I2C_SIZE(reg); | ||
143 | u8 category = I2C_CATEGORY(reg); | ||
144 | u8 cmd = I2C_COMMAND(reg); | ||
145 | struct i2c_msg msg[2]; | ||
146 | u8 wbuf[5]; | ||
147 | int ret; | ||
148 | |||
149 | if (!client->adapter) | ||
150 | return -ENODEV; | ||
151 | |||
152 | if (size != 1 && size != 2 && size != 4) { | ||
153 | v4l2_err(sd, "Wrong data size\n"); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | msg[0].addr = client->addr; | ||
158 | msg[0].flags = 0; | ||
159 | msg[0].len = 5; | ||
160 | msg[0].buf = wbuf; | ||
161 | wbuf[0] = 5; | ||
162 | wbuf[1] = M5MOLS_BYTE_READ; | ||
163 | wbuf[2] = category; | ||
164 | wbuf[3] = cmd; | ||
165 | wbuf[4] = size; | ||
166 | |||
167 | msg[1].addr = client->addr; | ||
168 | msg[1].flags = I2C_M_RD; | ||
169 | msg[1].len = size + 1; | ||
170 | msg[1].buf = rbuf; | ||
171 | |||
172 | /* minimum stabilization time */ | ||
173 | usleep_range(200, 200); | ||
174 | |||
175 | ret = i2c_transfer(client->adapter, msg, 2); | ||
176 | if (ret < 0) { | ||
177 | v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", | ||
178 | size, category, cmd, ret); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | *val = m5mols_swap_byte(&rbuf[1], size); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * m5mols_write - I2C command write function | ||
189 | * @reg: combination of size, category and command for the I2C packet | ||
190 | * @val: value to write | ||
191 | */ | ||
192 | int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) | ||
193 | { | ||
194 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
195 | u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; | ||
196 | u8 category = I2C_CATEGORY(reg); | ||
197 | u8 cmd = I2C_COMMAND(reg); | ||
198 | u8 size = I2C_SIZE(reg); | ||
199 | u32 *buf = (u32 *)&wbuf[4]; | ||
200 | struct i2c_msg msg[1]; | ||
201 | int ret; | ||
202 | |||
203 | if (!client->adapter) | ||
204 | return -ENODEV; | ||
205 | |||
206 | if (size != 1 && size != 2 && size != 4) { | ||
207 | v4l2_err(sd, "Wrong data size\n"); | ||
208 | return -EINVAL; | ||
209 | } | ||
210 | |||
211 | msg->addr = client->addr; | ||
212 | msg->flags = 0; | ||
213 | msg->len = (u16)size + 4; | ||
214 | msg->buf = wbuf; | ||
215 | wbuf[0] = size + 4; | ||
216 | wbuf[1] = M5MOLS_BYTE_WRITE; | ||
217 | wbuf[2] = category; | ||
218 | wbuf[3] = cmd; | ||
219 | |||
220 | *buf = m5mols_swap_byte((u8 *)&val, size); | ||
221 | |||
222 | usleep_range(200, 200); | ||
223 | |||
224 | ret = i2c_transfer(client->adapter, msg, 1); | ||
225 | if (ret < 0) { | ||
226 | v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n", | ||
227 | size, category, cmd, ret); | ||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask) | ||
235 | { | ||
236 | u32 busy, i; | ||
237 | int ret; | ||
238 | |||
239 | for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) { | ||
240 | ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy); | ||
241 | if (ret < 0) | ||
242 | return ret; | ||
243 | if ((busy & mask) == mask) | ||
244 | return 0; | ||
245 | } | ||
246 | return -EBUSY; | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts | ||
251 | * | ||
252 | * Before writing desired interrupt value the INT_FACTOR register should | ||
253 | * be read to clear pending interrupts. | ||
254 | */ | ||
255 | int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg) | ||
256 | { | ||
257 | struct m5mols_info *info = to_m5mols(sd); | ||
258 | u32 mask = is_available_af(info) ? REG_INT_AF : 0; | ||
259 | u32 dummy; | ||
260 | int ret; | ||
261 | |||
262 | ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy); | ||
263 | if (!ret) | ||
264 | ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); | ||
265 | return ret; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * m5mols_reg_mode - Write the mode and check busy status | ||
270 | * | ||
271 | * It always accompanies a little delay changing the M-5MOLS mode, so it is | ||
272 | * needed checking current busy status to guarantee right mode. | ||
273 | */ | ||
274 | static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode) | ||
275 | { | ||
276 | int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); | ||
277 | |||
278 | return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode); | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * m5mols_mode - manage the M-5MOLS's mode | ||
283 | * @mode: the required operation mode | ||
284 | * | ||
285 | * The commands of M-5MOLS are grouped into specific modes. Each functionality | ||
286 | * can be guaranteed only when the sensor is operating in mode which which | ||
287 | * a command belongs to. | ||
288 | */ | ||
289 | int m5mols_mode(struct m5mols_info *info, u32 mode) | ||
290 | { | ||
291 | struct v4l2_subdev *sd = &info->sd; | ||
292 | int ret = -EINVAL; | ||
293 | u32 reg; | ||
294 | |||
295 | if (mode < REG_PARAMETER && mode > REG_CAPTURE) | ||
296 | return ret; | ||
297 | |||
298 | ret = m5mols_read(sd, SYSTEM_SYSMODE, ®); | ||
299 | if ((!ret && reg == mode) || ret) | ||
300 | return ret; | ||
301 | |||
302 | switch (reg) { | ||
303 | case REG_PARAMETER: | ||
304 | ret = m5mols_reg_mode(sd, REG_MONITOR); | ||
305 | if (!ret && mode == REG_MONITOR) | ||
306 | break; | ||
307 | if (!ret) | ||
308 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | ||
309 | break; | ||
310 | |||
311 | case REG_MONITOR: | ||
312 | if (mode == REG_PARAMETER) { | ||
313 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | ||
314 | break; | ||
315 | } | ||
316 | |||
317 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | ||
318 | break; | ||
319 | |||
320 | case REG_CAPTURE: | ||
321 | ret = m5mols_reg_mode(sd, REG_MONITOR); | ||
322 | if (!ret && mode == REG_MONITOR) | ||
323 | break; | ||
324 | if (!ret) | ||
325 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | ||
326 | break; | ||
327 | |||
328 | default: | ||
329 | v4l2_warn(sd, "Wrong mode: %d\n", mode); | ||
330 | } | ||
331 | |||
332 | if (!ret) | ||
333 | info->mode = mode; | ||
334 | |||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * m5mols_get_version - retrieve full revisions information of M-5MOLS | ||
340 | * | ||
341 | * The version information includes revisions of hardware and firmware, | ||
342 | * AutoFocus alghorithm version and the version string. | ||
343 | */ | ||
344 | static int m5mols_get_version(struct v4l2_subdev *sd) | ||
345 | { | ||
346 | struct m5mols_info *info = to_m5mols(sd); | ||
347 | union { | ||
348 | struct m5mols_version ver; | ||
349 | u8 bytes[VERSION_SIZE]; | ||
350 | } version; | ||
351 | u32 *value; | ||
352 | u8 cmd = CAT0_VER_CUSTOMER; | ||
353 | int ret; | ||
354 | |||
355 | do { | ||
356 | value = (u32 *)&version.bytes[cmd]; | ||
357 | ret = m5mols_read(sd, SYSTEM_CMD(cmd), value); | ||
358 | if (ret) | ||
359 | return ret; | ||
360 | } while (cmd++ != CAT0_VER_AWB); | ||
361 | |||
362 | do { | ||
363 | value = (u32 *)&version.bytes[cmd]; | ||
364 | ret = m5mols_read(sd, SYSTEM_VER_STRING, value); | ||
365 | if (ret) | ||
366 | return ret; | ||
367 | if (cmd >= VERSION_SIZE - 1) | ||
368 | return -EINVAL; | ||
369 | } while (version.bytes[cmd++]); | ||
370 | |||
371 | value = (u32 *)&version.bytes[cmd]; | ||
372 | ret = m5mols_read(sd, AF_VERSION, value); | ||
373 | if (ret) | ||
374 | return ret; | ||
375 | |||
376 | /* store version information swapped for being readable */ | ||
377 | info->ver = version.ver; | ||
378 | info->ver.fw = be16_to_cpu(info->ver.fw); | ||
379 | info->ver.hw = be16_to_cpu(info->ver.hw); | ||
380 | info->ver.param = be16_to_cpu(info->ver.param); | ||
381 | info->ver.awb = be16_to_cpu(info->ver.awb); | ||
382 | |||
383 | v4l2_info(sd, "Manufacturer\t[%s]\n", | ||
384 | is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? | ||
385 | "Samsung Electro-Machanics" : | ||
386 | is_manufacturer(info, REG_SAMSUNG_OPTICS) ? | ||
387 | "Samsung Fiber-Optics" : | ||
388 | is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? | ||
389 | "Samsung Techwin" : "None"); | ||
390 | v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", | ||
391 | info->ver.customer, info->ver.project); | ||
392 | |||
393 | if (!is_available_af(info)) | ||
394 | v4l2_info(sd, "No support Auto Focus on this firmware\n"); | ||
395 | |||
396 | return ret; | ||
397 | } | ||
398 | |||
399 | /** | ||
400 | * __find_restype - Lookup M-5MOLS resolution type according to pixel code | ||
401 | * @code: pixel code | ||
402 | */ | ||
403 | static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) | ||
404 | { | ||
405 | enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; | ||
406 | |||
407 | do { | ||
408 | if (code == m5mols_default_ffmt[type].code) | ||
409 | return type; | ||
410 | } while (type++ != SIZE_DEFAULT_FFMT); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * __find_resolution - Lookup preset and type of M-5MOLS's resolution | ||
417 | * @mf: pixel format to find/negotiate the resolution preset for | ||
418 | * @type: M-5MOLS resolution type | ||
419 | * @resolution: M-5MOLS resolution preset register value | ||
420 | * | ||
421 | * Find nearest resolution matching resolution preset and adjust mf | ||
422 | * to supported values. | ||
423 | */ | ||
424 | static int __find_resolution(struct v4l2_subdev *sd, | ||
425 | struct v4l2_mbus_framefmt *mf, | ||
426 | enum m5mols_restype *type, | ||
427 | u32 *resolution) | ||
428 | { | ||
429 | const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; | ||
430 | const struct m5mols_resolution *match = NULL; | ||
431 | enum m5mols_restype stype = __find_restype(mf->code); | ||
432 | int i = ARRAY_SIZE(m5mols_reg_res); | ||
433 | unsigned int min_err = ~0; | ||
434 | |||
435 | while (i--) { | ||
436 | int err; | ||
437 | if (stype == fsize->type) { | ||
438 | err = abs(fsize->width - mf->width) | ||
439 | + abs(fsize->height - mf->height); | ||
440 | |||
441 | if (err < min_err) { | ||
442 | min_err = err; | ||
443 | match = fsize; | ||
444 | } | ||
445 | } | ||
446 | fsize++; | ||
447 | } | ||
448 | if (match) { | ||
449 | mf->width = match->width; | ||
450 | mf->height = match->height; | ||
451 | *resolution = match->reg; | ||
452 | *type = stype; | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | return -EINVAL; | ||
457 | } | ||
458 | |||
459 | static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, | ||
460 | struct v4l2_subdev_fh *fh, | ||
461 | enum v4l2_subdev_format_whence which, | ||
462 | enum m5mols_restype type) | ||
463 | { | ||
464 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
465 | return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; | ||
466 | |||
467 | return &info->ffmt[type]; | ||
468 | } | ||
469 | |||
470 | static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
471 | struct v4l2_subdev_format *fmt) | ||
472 | { | ||
473 | struct m5mols_info *info = to_m5mols(sd); | ||
474 | struct v4l2_mbus_framefmt *format; | ||
475 | |||
476 | if (fmt->pad != 0) | ||
477 | return -EINVAL; | ||
478 | |||
479 | format = __find_format(info, fh, fmt->which, info->res_type); | ||
480 | if (!format) | ||
481 | return -EINVAL; | ||
482 | |||
483 | fmt->format = *format; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
488 | struct v4l2_subdev_format *fmt) | ||
489 | { | ||
490 | struct m5mols_info *info = to_m5mols(sd); | ||
491 | struct v4l2_mbus_framefmt *format = &fmt->format; | ||
492 | struct v4l2_mbus_framefmt *sfmt; | ||
493 | enum m5mols_restype type; | ||
494 | u32 resolution = 0; | ||
495 | int ret; | ||
496 | |||
497 | if (fmt->pad != 0) | ||
498 | return -EINVAL; | ||
499 | |||
500 | ret = __find_resolution(sd, format, &type, &resolution); | ||
501 | if (ret < 0) | ||
502 | return ret; | ||
503 | |||
504 | sfmt = __find_format(info, fh, fmt->which, type); | ||
505 | if (!sfmt) | ||
506 | return 0; | ||
507 | |||
508 | *sfmt = m5mols_default_ffmt[type]; | ||
509 | sfmt->width = format->width; | ||
510 | sfmt->height = format->height; | ||
511 | |||
512 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
513 | info->resolution = resolution; | ||
514 | info->code = format->code; | ||
515 | info->res_type = type; | ||
516 | } | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, | ||
522 | struct v4l2_subdev_fh *fh, | ||
523 | struct v4l2_subdev_mbus_code_enum *code) | ||
524 | { | ||
525 | if (!code || code->index >= SIZE_DEFAULT_FFMT) | ||
526 | return -EINVAL; | ||
527 | |||
528 | code->code = m5mols_default_ffmt[code->index].code; | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static struct v4l2_subdev_pad_ops m5mols_pad_ops = { | ||
534 | .enum_mbus_code = m5mols_enum_mbus_code, | ||
535 | .get_fmt = m5mols_get_fmt, | ||
536 | .set_fmt = m5mols_set_fmt, | ||
537 | }; | ||
538 | |||
539 | /** | ||
540 | * m5mols_sync_controls - Apply default scene mode and the current controls | ||
541 | * | ||
542 | * This is used only streaming for syncing between v4l2_ctrl framework and | ||
543 | * m5mols's controls. First, do the scenemode to the sensor, then call | ||
544 | * v4l2_ctrl_handler_setup. It can be same between some commands and | ||
545 | * the scenemode's in the default v4l2_ctrls. But, such commands of control | ||
546 | * should be prior to the scenemode's one. | ||
547 | */ | ||
548 | int m5mols_sync_controls(struct m5mols_info *info) | ||
549 | { | ||
550 | int ret = -EINVAL; | ||
551 | |||
552 | if (!is_ctrl_synced(info)) { | ||
553 | ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); | ||
554 | if (ret) | ||
555 | return ret; | ||
556 | |||
557 | v4l2_ctrl_handler_setup(&info->handle); | ||
558 | info->ctrl_sync = true; | ||
559 | } | ||
560 | |||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * m5mols_start_monitor - Start the monitor mode | ||
566 | * | ||
567 | * Before applying the controls setup the resolution and frame rate | ||
568 | * in PARAMETER mode, and then switch over to MONITOR mode. | ||
569 | */ | ||
570 | static int m5mols_start_monitor(struct m5mols_info *info) | ||
571 | { | ||
572 | struct v4l2_subdev *sd = &info->sd; | ||
573 | int ret; | ||
574 | |||
575 | ret = m5mols_mode(info, REG_PARAMETER); | ||
576 | if (!ret) | ||
577 | ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); | ||
578 | if (!ret) | ||
579 | ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); | ||
580 | if (!ret) | ||
581 | ret = m5mols_mode(info, REG_MONITOR); | ||
582 | if (!ret) | ||
583 | ret = m5mols_sync_controls(info); | ||
584 | |||
585 | return ret; | ||
586 | } | ||
587 | |||
588 | static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) | ||
589 | { | ||
590 | struct m5mols_info *info = to_m5mols(sd); | ||
591 | |||
592 | if (enable) { | ||
593 | int ret = -EINVAL; | ||
594 | |||
595 | if (is_code(info->code, M5MOLS_RESTYPE_MONITOR)) | ||
596 | ret = m5mols_start_monitor(info); | ||
597 | if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE)) | ||
598 | ret = m5mols_start_capture(info); | ||
599 | |||
600 | return ret; | ||
601 | } | ||
602 | |||
603 | return m5mols_mode(info, REG_PARAMETER); | ||
604 | } | ||
605 | |||
606 | static const struct v4l2_subdev_video_ops m5mols_video_ops = { | ||
607 | .s_stream = m5mols_s_stream, | ||
608 | }; | ||
609 | |||
610 | static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) | ||
611 | { | ||
612 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
613 | struct m5mols_info *info = to_m5mols(sd); | ||
614 | int ret; | ||
615 | |||
616 | info->mode_save = info->mode; | ||
617 | |||
618 | ret = m5mols_mode(info, REG_PARAMETER); | ||
619 | if (!ret) | ||
620 | ret = m5mols_set_ctrl(ctrl); | ||
621 | if (!ret) | ||
622 | ret = m5mols_mode(info, info->mode_save); | ||
623 | |||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { | ||
628 | .s_ctrl = m5mols_s_ctrl, | ||
629 | }; | ||
630 | |||
631 | static int m5mols_sensor_power(struct m5mols_info *info, bool enable) | ||
632 | { | ||
633 | struct v4l2_subdev *sd = &info->sd; | ||
634 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
635 | const struct m5mols_platform_data *pdata = info->pdata; | ||
636 | int ret; | ||
637 | |||
638 | if (enable) { | ||
639 | if (is_powered(info)) | ||
640 | return 0; | ||
641 | |||
642 | if (info->set_power) { | ||
643 | ret = info->set_power(&client->dev, 1); | ||
644 | if (ret) | ||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); | ||
649 | if (ret) { | ||
650 | info->set_power(&client->dev, 0); | ||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); | ||
655 | usleep_range(1000, 1000); | ||
656 | info->power = true; | ||
657 | |||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | if (!is_powered(info)) | ||
662 | return 0; | ||
663 | |||
664 | ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); | ||
665 | if (ret) | ||
666 | return ret; | ||
667 | |||
668 | if (info->set_power) | ||
669 | info->set_power(&client->dev, 0); | ||
670 | |||
671 | gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); | ||
672 | usleep_range(1000, 1000); | ||
673 | info->power = false; | ||
674 | |||
675 | return ret; | ||
676 | } | ||
677 | |||
678 | /* m5mols_update_fw - optional firmware update routine */ | ||
679 | int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, | ||
680 | int (*set_power)(struct m5mols_info *, bool)) | ||
681 | { | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | /** | ||
686 | * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core. | ||
687 | * | ||
688 | * Booting internal ARM core makes the M-5MOLS is ready for getting commands | ||
689 | * with I2C. It's the first thing to be done after it powered up. It must wait | ||
690 | * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting. | ||
691 | */ | ||
692 | static int m5mols_sensor_armboot(struct v4l2_subdev *sd) | ||
693 | { | ||
694 | int ret; | ||
695 | |||
696 | ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); | ||
697 | if (ret < 0) | ||
698 | return ret; | ||
699 | |||
700 | msleep(520); | ||
701 | |||
702 | ret = m5mols_get_version(sd); | ||
703 | if (!ret) | ||
704 | ret = m5mols_update_fw(sd, m5mols_sensor_power); | ||
705 | if (ret) | ||
706 | return ret; | ||
707 | |||
708 | v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); | ||
709 | |||
710 | ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); | ||
711 | if (!ret) | ||
712 | ret = m5mols_enable_interrupt(sd, REG_INT_AF); | ||
713 | |||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | static int m5mols_init_controls(struct m5mols_info *info) | ||
718 | { | ||
719 | struct v4l2_subdev *sd = &info->sd; | ||
720 | u16 max_exposure; | ||
721 | u16 step_zoom; | ||
722 | int ret; | ||
723 | |||
724 | /* Determine value's range & step of controls for various FW version */ | ||
725 | ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure); | ||
726 | if (!ret) | ||
727 | step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; | ||
728 | if (ret) | ||
729 | return ret; | ||
730 | |||
731 | v4l2_ctrl_handler_init(&info->handle, 6); | ||
732 | info->autowb = v4l2_ctrl_new_std(&info->handle, | ||
733 | &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, | ||
734 | 0, 1, 1, 0); | ||
735 | info->saturation = v4l2_ctrl_new_std(&info->handle, | ||
736 | &m5mols_ctrl_ops, V4L2_CID_SATURATION, | ||
737 | 1, 5, 1, 3); | ||
738 | info->zoom = v4l2_ctrl_new_std(&info->handle, | ||
739 | &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, | ||
740 | 1, 70, step_zoom, 1); | ||
741 | info->exposure = v4l2_ctrl_new_std(&info->handle, | ||
742 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, | ||
743 | 0, max_exposure, 1, (int)max_exposure/2); | ||
744 | info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, | ||
745 | &m5mols_ctrl_ops, V4L2_CID_COLORFX, | ||
746 | 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); | ||
747 | info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, | ||
748 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, | ||
749 | 1, 0, V4L2_EXPOSURE_MANUAL); | ||
750 | |||
751 | sd->ctrl_handler = &info->handle; | ||
752 | if (info->handle.error) { | ||
753 | v4l2_err(sd, "Failed to initialize controls: %d\n", ret); | ||
754 | v4l2_ctrl_handler_free(&info->handle); | ||
755 | return info->handle.error; | ||
756 | } | ||
757 | |||
758 | v4l2_ctrl_cluster(2, &info->autoexposure); | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | /** | ||
764 | * m5mols_s_power - Main sensor power control function | ||
765 | * | ||
766 | * To prevent breaking the lens when the sensor is powered off the Soft-Landing | ||
767 | * algorithm is called where available. The Soft-Landing algorithm availability | ||
768 | * dependends on the firmware provider. | ||
769 | */ | ||
770 | static int m5mols_s_power(struct v4l2_subdev *sd, int on) | ||
771 | { | ||
772 | struct m5mols_info *info = to_m5mols(sd); | ||
773 | int ret; | ||
774 | |||
775 | if (on) { | ||
776 | ret = m5mols_sensor_power(info, true); | ||
777 | if (!ret) | ||
778 | ret = m5mols_sensor_armboot(sd); | ||
779 | if (!ret) | ||
780 | ret = m5mols_init_controls(info); | ||
781 | if (ret) | ||
782 | return ret; | ||
783 | |||
784 | info->ffmt[M5MOLS_RESTYPE_MONITOR] = | ||
785 | m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR]; | ||
786 | info->ffmt[M5MOLS_RESTYPE_CAPTURE] = | ||
787 | m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE]; | ||
788 | return ret; | ||
789 | } | ||
790 | |||
791 | if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { | ||
792 | ret = m5mols_mode(info, REG_MONITOR); | ||
793 | if (!ret) | ||
794 | ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); | ||
795 | if (!ret) | ||
796 | ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); | ||
797 | if (!ret) | ||
798 | ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS, | ||
799 | REG_AF_IDLE); | ||
800 | if (!ret) | ||
801 | v4l2_info(sd, "Success soft-landing lens\n"); | ||
802 | } | ||
803 | |||
804 | ret = m5mols_sensor_power(info, false); | ||
805 | if (!ret) { | ||
806 | v4l2_ctrl_handler_free(&info->handle); | ||
807 | info->ctrl_sync = false; | ||
808 | } | ||
809 | |||
810 | return ret; | ||
811 | } | ||
812 | |||
813 | static int m5mols_log_status(struct v4l2_subdev *sd) | ||
814 | { | ||
815 | struct m5mols_info *info = to_m5mols(sd); | ||
816 | |||
817 | v4l2_ctrl_handler_log_status(&info->handle, sd->name); | ||
818 | |||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | static const struct v4l2_subdev_core_ops m5mols_core_ops = { | ||
823 | .s_power = m5mols_s_power, | ||
824 | .g_ctrl = v4l2_subdev_g_ctrl, | ||
825 | .s_ctrl = v4l2_subdev_s_ctrl, | ||
826 | .queryctrl = v4l2_subdev_queryctrl, | ||
827 | .querymenu = v4l2_subdev_querymenu, | ||
828 | .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
829 | .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
830 | .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
831 | .log_status = m5mols_log_status, | ||
832 | }; | ||
833 | |||
834 | static const struct v4l2_subdev_ops m5mols_ops = { | ||
835 | .core = &m5mols_core_ops, | ||
836 | .pad = &m5mols_pad_ops, | ||
837 | .video = &m5mols_video_ops, | ||
838 | }; | ||
839 | |||
840 | static void m5mols_irq_work(struct work_struct *work) | ||
841 | { | ||
842 | struct m5mols_info *info = | ||
843 | container_of(work, struct m5mols_info, work_irq); | ||
844 | struct v4l2_subdev *sd = &info->sd; | ||
845 | u32 reg; | ||
846 | int ret; | ||
847 | |||
848 | if (!is_powered(info) || | ||
849 | m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt)) | ||
850 | return; | ||
851 | |||
852 | switch (info->interrupt & REG_INT_MASK) { | ||
853 | case REG_INT_AF: | ||
854 | if (!is_available_af(info)) | ||
855 | break; | ||
856 | ret = m5mols_read(sd, AF_STATUS, ®); | ||
857 | v4l2_dbg(2, m5mols_debug, sd, "AF %s\n", | ||
858 | reg == REG_AF_FAIL ? "Failed" : | ||
859 | reg == REG_AF_SUCCESS ? "Success" : | ||
860 | reg == REG_AF_IDLE ? "Idle" : "Busy"); | ||
861 | break; | ||
862 | case REG_INT_CAPTURE: | ||
863 | if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags)) | ||
864 | wake_up_interruptible(&info->irq_waitq); | ||
865 | |||
866 | v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n"); | ||
867 | break; | ||
868 | default: | ||
869 | v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg); | ||
870 | break; | ||
871 | }; | ||
872 | } | ||
873 | |||
874 | static irqreturn_t m5mols_irq_handler(int irq, void *data) | ||
875 | { | ||
876 | struct v4l2_subdev *sd = data; | ||
877 | struct m5mols_info *info = to_m5mols(sd); | ||
878 | |||
879 | schedule_work(&info->work_irq); | ||
880 | |||
881 | return IRQ_HANDLED; | ||
882 | } | ||
883 | |||
884 | static int __devinit m5mols_probe(struct i2c_client *client, | ||
885 | const struct i2c_device_id *id) | ||
886 | { | ||
887 | const struct m5mols_platform_data *pdata = client->dev.platform_data; | ||
888 | struct m5mols_info *info; | ||
889 | struct v4l2_subdev *sd; | ||
890 | int ret; | ||
891 | |||
892 | if (pdata == NULL) { | ||
893 | dev_err(&client->dev, "No platform data\n"); | ||
894 | return -EINVAL; | ||
895 | } | ||
896 | |||
897 | if (!gpio_is_valid(pdata->gpio_reset)) { | ||
898 | dev_err(&client->dev, "No valid RESET GPIO specified\n"); | ||
899 | return -EINVAL; | ||
900 | } | ||
901 | |||
902 | if (!pdata->irq) { | ||
903 | dev_err(&client->dev, "Interrupt not assigned\n"); | ||
904 | return -EINVAL; | ||
905 | } | ||
906 | |||
907 | info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); | ||
908 | if (!info) | ||
909 | return -ENOMEM; | ||
910 | |||
911 | info->pdata = pdata; | ||
912 | info->set_power = pdata->set_power; | ||
913 | |||
914 | ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); | ||
915 | if (ret) { | ||
916 | dev_err(&client->dev, "Failed to request gpio: %d\n", ret); | ||
917 | goto out_free; | ||
918 | } | ||
919 | gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); | ||
920 | |||
921 | ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); | ||
922 | if (ret) { | ||
923 | dev_err(&client->dev, "Failed to get regulators: %d\n", ret); | ||
924 | goto out_gpio; | ||
925 | } | ||
926 | |||
927 | sd = &info->sd; | ||
928 | strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); | ||
929 | v4l2_i2c_subdev_init(sd, client, &m5mols_ops); | ||
930 | |||
931 | info->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
932 | ret = media_entity_init(&sd->entity, 1, &info->pad, 0); | ||
933 | if (ret < 0) | ||
934 | goto out_reg; | ||
935 | sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; | ||
936 | |||
937 | init_waitqueue_head(&info->irq_waitq); | ||
938 | INIT_WORK(&info->work_irq, m5mols_irq_work); | ||
939 | ret = request_irq(pdata->irq, m5mols_irq_handler, | ||
940 | IRQF_TRIGGER_RISING, MODULE_NAME, sd); | ||
941 | if (ret) { | ||
942 | dev_err(&client->dev, "Interrupt request failed: %d\n", ret); | ||
943 | goto out_me; | ||
944 | } | ||
945 | info->res_type = M5MOLS_RESTYPE_MONITOR; | ||
946 | return 0; | ||
947 | out_me: | ||
948 | media_entity_cleanup(&sd->entity); | ||
949 | out_reg: | ||
950 | regulator_bulk_free(ARRAY_SIZE(supplies), supplies); | ||
951 | out_gpio: | ||
952 | gpio_free(pdata->gpio_reset); | ||
953 | out_free: | ||
954 | kfree(info); | ||
955 | return ret; | ||
956 | } | ||
957 | |||
958 | static int __devexit m5mols_remove(struct i2c_client *client) | ||
959 | { | ||
960 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
961 | struct m5mols_info *info = to_m5mols(sd); | ||
962 | |||
963 | v4l2_device_unregister_subdev(sd); | ||
964 | free_irq(info->pdata->irq, sd); | ||
965 | |||
966 | regulator_bulk_free(ARRAY_SIZE(supplies), supplies); | ||
967 | gpio_free(info->pdata->gpio_reset); | ||
968 | media_entity_cleanup(&sd->entity); | ||
969 | kfree(info); | ||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | static const struct i2c_device_id m5mols_id[] = { | ||
974 | { MODULE_NAME, 0 }, | ||
975 | { }, | ||
976 | }; | ||
977 | MODULE_DEVICE_TABLE(i2c, m5mols_id); | ||
978 | |||
979 | static struct i2c_driver m5mols_i2c_driver = { | ||
980 | .driver = { | ||
981 | .name = MODULE_NAME, | ||
982 | }, | ||
983 | .probe = m5mols_probe, | ||
984 | .remove = __devexit_p(m5mols_remove), | ||
985 | .id_table = m5mols_id, | ||
986 | }; | ||
987 | |||
988 | static int __init m5mols_mod_init(void) | ||
989 | { | ||
990 | return i2c_add_driver(&m5mols_i2c_driver); | ||
991 | } | ||
992 | |||
993 | static void __exit m5mols_mod_exit(void) | ||
994 | { | ||
995 | i2c_del_driver(&m5mols_i2c_driver); | ||
996 | } | ||
997 | |||
998 | module_init(m5mols_mod_init); | ||
999 | module_exit(m5mols_mod_exit); | ||
1000 | |||
1001 | MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); | ||
1002 | MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); | ||
1003 | MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); | ||
1004 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h new file mode 100644 index 000000000000..b83e36fc6ac6 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_reg.h | |||
@@ -0,0 +1,399 @@ | |||
1 | /* | ||
2 | * Register map for M-5MOLS 8M Pixel camera sensor with ISP | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * Author: HeungJun Kim, riverful.kim@samsung.com | ||
6 | * | ||
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
8 | * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #ifndef M5MOLS_REG_H | ||
17 | #define M5MOLS_REG_H | ||
18 | |||
19 | #define M5MOLS_I2C_MAX_SIZE 4 | ||
20 | #define M5MOLS_BYTE_READ 0x01 | ||
21 | #define M5MOLS_BYTE_WRITE 0x02 | ||
22 | |||
23 | #define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) | ||
24 | #define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) | ||
25 | #define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) | ||
26 | #define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) | ||
27 | |||
28 | /* | ||
29 | * Category section register | ||
30 | * | ||
31 | * The category means set including relevant command of M-5MOLS. | ||
32 | */ | ||
33 | #define CAT_SYSTEM 0x00 | ||
34 | #define CAT_PARAM 0x01 | ||
35 | #define CAT_MONITOR 0x02 | ||
36 | #define CAT_AE 0x03 | ||
37 | #define CAT_WB 0x06 | ||
38 | #define CAT_EXIF 0x07 | ||
39 | #define CAT_FD 0x09 | ||
40 | #define CAT_LENS 0x0a | ||
41 | #define CAT_CAPT_PARM 0x0b | ||
42 | #define CAT_CAPT_CTRL 0x0c | ||
43 | #define CAT_FLASH 0x0f /* related to FW, revisions, booting */ | ||
44 | |||
45 | /* | ||
46 | * Category 0 - SYSTEM mode | ||
47 | * | ||
48 | * The SYSTEM mode in the M-5MOLS means area available to handle with the whole | ||
49 | * & all-round system of sensor. It deals with version/interrupt/setting mode & | ||
50 | * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by | ||
51 | * packaging & manufacturer, even the customer and project code. And the | ||
52 | * function details may vary among them. The version information helps to | ||
53 | * determine what methods shall be used in the driver. | ||
54 | * | ||
55 | * There is many registers between customer version address and awb one. For | ||
56 | * more specific contents, see definition if file m5mols.h. | ||
57 | */ | ||
58 | #define CAT0_VER_CUSTOMER 0x00 /* customer version */ | ||
59 | #define CAT0_VER_AWB 0x09 /* Auto WB version */ | ||
60 | #define CAT0_VER_STRING 0x0a /* string including M-5MOLS */ | ||
61 | #define CAT0_SYSMODE 0x0b /* SYSTEM mode register */ | ||
62 | #define CAT0_STATUS 0x0c /* SYSTEM mode status register */ | ||
63 | #define CAT0_INT_FACTOR 0x10 /* interrupt pending register */ | ||
64 | #define CAT0_INT_ENABLE 0x11 /* interrupt enable register */ | ||
65 | |||
66 | #define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1) | ||
67 | #define REG_SYSINIT 0x00 /* SYSTEM mode */ | ||
68 | #define REG_PARAMETER 0x01 /* PARAMETER mode */ | ||
69 | #define REG_MONITOR 0x02 /* MONITOR mode */ | ||
70 | #define REG_CAPTURE 0x03 /* CAPTURE mode */ | ||
71 | |||
72 | #define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) | ||
73 | #define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1) | ||
74 | #define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ | ||
75 | #define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ | ||
76 | #define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ | ||
77 | |||
78 | #define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1) | ||
79 | #define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1) | ||
80 | #define REG_INT_MODE (1 << 0) | ||
81 | #define REG_INT_AF (1 << 1) | ||
82 | #define REG_INT_ZOOM (1 << 2) | ||
83 | #define REG_INT_CAPTURE (1 << 3) | ||
84 | #define REG_INT_FRAMESYNC (1 << 4) | ||
85 | #define REG_INT_FD (1 << 5) | ||
86 | #define REG_INT_LENS_INIT (1 << 6) | ||
87 | #define REG_INT_SOUND (1 << 7) | ||
88 | #define REG_INT_MASK 0x0f | ||
89 | |||
90 | /* | ||
91 | * category 1 - PARAMETER mode | ||
92 | * | ||
93 | * This category supports function of camera features of M-5MOLS. It means we | ||
94 | * can handle with preview(MONITOR) resolution size/frame per second/interface | ||
95 | * between the sensor and the Application Processor/even the image effect. | ||
96 | */ | ||
97 | #define CAT1_DATA_INTERFACE 0x00 /* interface between sensor and AP */ | ||
98 | #define CAT1_MONITOR_SIZE 0x01 /* resolution at the MONITOR mode */ | ||
99 | #define CAT1_MONITOR_FPS 0x02 /* frame per second at this mode */ | ||
100 | #define CAT1_EFFECT 0x0b /* image effects */ | ||
101 | |||
102 | #define PARM_MON_SIZE I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1) | ||
103 | |||
104 | #define PARM_MON_FPS I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1) | ||
105 | #define REG_FPS_30 0x02 | ||
106 | |||
107 | #define PARM_INTERFACE I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1) | ||
108 | #define REG_INTERFACE_MIPI 0x02 | ||
109 | |||
110 | #define PARM_EFFECT I2C_REG(CAT_PARAM, CAT1_EFFECT, 1) | ||
111 | #define REG_EFFECT_OFF 0x00 | ||
112 | #define REG_EFFECT_NEGA 0x01 | ||
113 | #define REG_EFFECT_EMBOSS 0x06 | ||
114 | #define REG_EFFECT_OUTLINE 0x07 | ||
115 | #define REG_EFFECT_WATERCOLOR 0x08 | ||
116 | |||
117 | /* | ||
118 | * Category 2 - MONITOR mode | ||
119 | * | ||
120 | * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another | ||
121 | * mode named "Preview", but this preview mode is used at the case specific | ||
122 | * vider-recording mode. This mmode supports only YUYV format. On the other | ||
123 | * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are | ||
124 | * another options like zoom/color effect(different with effect in PARAMETER | ||
125 | * mode)/anti hand shaking algorithm. | ||
126 | */ | ||
127 | #define CAT2_ZOOM 0x01 /* set the zoom position & execute */ | ||
128 | #define CAT2_ZOOM_STEP 0x03 /* set the zoom step */ | ||
129 | #define CAT2_CFIXB 0x09 /* CB value for color effect */ | ||
130 | #define CAT2_CFIXR 0x0a /* CR value for color effect */ | ||
131 | #define CAT2_COLOR_EFFECT 0x0b /* set on/off of color effect */ | ||
132 | #define CAT2_CHROMA_LVL 0x0f /* set chroma level */ | ||
133 | #define CAT2_CHROMA_EN 0x10 /* set on/off of choroma */ | ||
134 | #define CAT2_EDGE_LVL 0x11 /* set sharpness level */ | ||
135 | #define CAT2_EDGE_EN 0x12 /* set on/off sharpness */ | ||
136 | #define CAT2_TONE_CTL 0x25 /* set tone color(contrast) */ | ||
137 | |||
138 | #define MON_ZOOM I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1) | ||
139 | |||
140 | #define MON_CFIXR I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1) | ||
141 | #define MON_CFIXB I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1) | ||
142 | #define REG_CFIXB_SEPIA 0xd8 | ||
143 | #define REG_CFIXR_SEPIA 0x18 | ||
144 | |||
145 | #define MON_EFFECT I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1) | ||
146 | #define REG_COLOR_EFFECT_OFF 0x00 | ||
147 | #define REG_COLOR_EFFECT_ON 0x01 | ||
148 | |||
149 | #define MON_CHROMA_EN I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1) | ||
150 | #define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1) | ||
151 | #define REG_CHROMA_OFF 0x00 | ||
152 | #define REG_CHROMA_ON 0x01 | ||
153 | |||
154 | #define MON_EDGE_EN I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1) | ||
155 | #define MON_EDGE_LVL I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1) | ||
156 | #define REG_EDGE_OFF 0x00 | ||
157 | #define REG_EDGE_ON 0x01 | ||
158 | |||
159 | #define MON_TONE_CTL I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1) | ||
160 | |||
161 | /* | ||
162 | * Category 3 - Auto Exposure | ||
163 | * | ||
164 | * The M-5MOLS exposure capbility is detailed as which is similar to digital | ||
165 | * camera. This category supports AE locking/various AE mode(range of exposure) | ||
166 | * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the | ||
167 | * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be | ||
168 | * different. So, this category also provide getting the max/min values. And, | ||
169 | * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. | ||
170 | */ | ||
171 | #define CAT3_AE_LOCK 0x00 /* locking Auto exposure */ | ||
172 | #define CAT3_AE_MODE 0x01 /* set AE mode, mode means range */ | ||
173 | #define CAT3_ISO 0x05 /* set ISO */ | ||
174 | #define CAT3_EV_PRESET_MONITOR 0x0a /* EV(scenemode) preset for MONITOR */ | ||
175 | #define CAT3_EV_PRESET_CAPTURE 0x0b /* EV(scenemode) preset for CAPTURE */ | ||
176 | #define CAT3_MANUAL_GAIN_MON 0x12 /* meteoring value for the MONITOR */ | ||
177 | #define CAT3_MAX_GAIN_MON 0x1a /* max gain value for the MONITOR */ | ||
178 | #define CAT3_MANUAL_GAIN_CAP 0x26 /* meteoring value for the CAPTURE */ | ||
179 | #define CAT3_AE_INDEX 0x38 /* AE index */ | ||
180 | |||
181 | #define AE_LOCK I2C_REG(CAT_AE, CAT3_AE_LOCK, 1) | ||
182 | #define REG_AE_UNLOCK 0x00 | ||
183 | #define REG_AE_LOCK 0x01 | ||
184 | |||
185 | #define AE_MODE I2C_REG(CAT_AE, CAT3_AE_MODE, 1) | ||
186 | #define REG_AE_OFF 0x00 /* AE off */ | ||
187 | #define REG_AE_ALL 0x01 /* calc AE in all block integral */ | ||
188 | #define REG_AE_CENTER 0x03 /* calc AE in center weighted */ | ||
189 | #define REG_AE_SPOT 0x06 /* calc AE in specific spot */ | ||
190 | |||
191 | #define AE_ISO I2C_REG(CAT_AE, CAT3_ISO, 1) | ||
192 | #define REG_ISO_AUTO 0x00 | ||
193 | #define REG_ISO_50 0x01 | ||
194 | #define REG_ISO_100 0x02 | ||
195 | #define REG_ISO_200 0x03 | ||
196 | #define REG_ISO_400 0x04 | ||
197 | #define REG_ISO_800 0x05 | ||
198 | |||
199 | #define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1) | ||
200 | #define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1) | ||
201 | #define REG_SCENE_NORMAL 0x00 | ||
202 | #define REG_SCENE_PORTRAIT 0x01 | ||
203 | #define REG_SCENE_LANDSCAPE 0x02 | ||
204 | #define REG_SCENE_SPORTS 0x03 | ||
205 | #define REG_SCENE_PARTY_INDOOR 0x04 | ||
206 | #define REG_SCENE_BEACH_SNOW 0x05 | ||
207 | #define REG_SCENE_SUNSET 0x06 | ||
208 | #define REG_SCENE_DAWN_DUSK 0x07 | ||
209 | #define REG_SCENE_FALL 0x08 | ||
210 | #define REG_SCENE_NIGHT 0x09 | ||
211 | #define REG_SCENE_AGAINST_LIGHT 0x0a | ||
212 | #define REG_SCENE_FIRE 0x0b | ||
213 | #define REG_SCENE_TEXT 0x0c | ||
214 | #define REG_SCENE_CANDLE 0x0d | ||
215 | |||
216 | #define AE_MAN_GAIN_MON I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2) | ||
217 | #define AE_MAX_GAIN_MON I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2) | ||
218 | #define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2) | ||
219 | |||
220 | #define AE_INDEX I2C_REG(CAT_AE, CAT3_AE_INDEX, 1) | ||
221 | #define REG_AE_INDEX_20_NEG 0x00 | ||
222 | #define REG_AE_INDEX_15_NEG 0x01 | ||
223 | #define REG_AE_INDEX_10_NEG 0x02 | ||
224 | #define REG_AE_INDEX_05_NEG 0x03 | ||
225 | #define REG_AE_INDEX_00 0x04 | ||
226 | #define REG_AE_INDEX_05_POS 0x05 | ||
227 | #define REG_AE_INDEX_10_POS 0x06 | ||
228 | #define REG_AE_INDEX_15_POS 0x07 | ||
229 | #define REG_AE_INDEX_20_POS 0x08 | ||
230 | |||
231 | /* | ||
232 | * Category 6 - White Balance | ||
233 | * | ||
234 | * This category provide AWB locking/mode/preset/speed/gain bias, etc. | ||
235 | */ | ||
236 | #define CAT6_AWB_LOCK 0x00 /* locking Auto Whitebalance */ | ||
237 | #define CAT6_AWB_MODE 0x02 /* set Auto or Manual */ | ||
238 | #define CAT6_AWB_MANUAL 0x03 /* set Manual(preset) value */ | ||
239 | |||
240 | #define AWB_LOCK I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1) | ||
241 | #define REG_AWB_UNLOCK 0x00 | ||
242 | #define REG_AWB_LOCK 0x01 | ||
243 | |||
244 | #define AWB_MODE I2C_REG(CAT_WB, CAT6_AWB_MODE, 1) | ||
245 | #define REG_AWB_AUTO 0x01 /* AWB off */ | ||
246 | #define REG_AWB_PRESET 0x02 /* AWB preset */ | ||
247 | |||
248 | #define AWB_MANUAL I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1) | ||
249 | #define REG_AWB_INCANDESCENT 0x01 | ||
250 | #define REG_AWB_FLUORESCENT_1 0x02 | ||
251 | #define REG_AWB_FLUORESCENT_2 0x03 | ||
252 | #define REG_AWB_DAYLIGHT 0x04 | ||
253 | #define REG_AWB_CLOUDY 0x05 | ||
254 | #define REG_AWB_SHADE 0x06 | ||
255 | #define REG_AWB_HORIZON 0x07 | ||
256 | #define REG_AWB_LEDLIGHT 0x09 | ||
257 | |||
258 | /* | ||
259 | * Category 7 - EXIF information | ||
260 | */ | ||
261 | #define CAT7_INFO_EXPTIME_NU 0x00 | ||
262 | #define CAT7_INFO_EXPTIME_DE 0x04 | ||
263 | #define CAT7_INFO_TV_NU 0x08 | ||
264 | #define CAT7_INFO_TV_DE 0x0c | ||
265 | #define CAT7_INFO_AV_NU 0x10 | ||
266 | #define CAT7_INFO_AV_DE 0x14 | ||
267 | #define CAT7_INFO_BV_NU 0x18 | ||
268 | #define CAT7_INFO_BV_DE 0x1c | ||
269 | #define CAT7_INFO_EBV_NU 0x20 | ||
270 | #define CAT7_INFO_EBV_DE 0x24 | ||
271 | #define CAT7_INFO_ISO 0x28 | ||
272 | #define CAT7_INFO_FLASH 0x2a | ||
273 | #define CAT7_INFO_SDR 0x2c | ||
274 | #define CAT7_INFO_QVAL 0x2e | ||
275 | |||
276 | #define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4) | ||
277 | #define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4) | ||
278 | #define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4) | ||
279 | #define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4) | ||
280 | #define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4) | ||
281 | #define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4) | ||
282 | #define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4) | ||
283 | #define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4) | ||
284 | #define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4) | ||
285 | #define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4) | ||
286 | #define EXIF_INFO_ISO I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2) | ||
287 | #define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2) | ||
288 | #define EXIF_INFO_SDR I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2) | ||
289 | #define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2) | ||
290 | |||
291 | /* | ||
292 | * Category 9 - Face Detection | ||
293 | */ | ||
294 | #define CAT9_FD_CTL 0x00 | ||
295 | |||
296 | #define FD_CTL I2C_REG(CAT_FD, CAT9_FD_CTL, 1) | ||
297 | #define BIT_FD_EN 0 | ||
298 | #define BIT_FD_DRAW_FACE_FRAME 4 | ||
299 | #define BIT_FD_DRAW_SMILE_LVL 6 | ||
300 | #define REG_FD(shift) (1 << shift) | ||
301 | #define REG_FD_OFF 0x0 | ||
302 | |||
303 | /* | ||
304 | * Category A - Lens Parameter | ||
305 | */ | ||
306 | #define CATA_AF_MODE 0x01 | ||
307 | #define CATA_AF_EXECUTE 0x02 | ||
308 | #define CATA_AF_STATUS 0x03 | ||
309 | #define CATA_AF_VERSION 0x0a | ||
310 | |||
311 | #define AF_MODE I2C_REG(CAT_LENS, CATA_AF_MODE, 1) | ||
312 | #define REG_AF_NORMAL 0x00 /* Normal AF, one time */ | ||
313 | #define REG_AF_MACRO 0x01 /* Macro AF, one time */ | ||
314 | #define REG_AF_POWEROFF 0x07 | ||
315 | |||
316 | #define AF_EXECUTE I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1) | ||
317 | #define REG_AF_STOP 0x00 | ||
318 | #define REG_AF_EXE_AUTO 0x01 | ||
319 | #define REG_AF_EXE_CAF 0x02 | ||
320 | |||
321 | #define AF_STATUS I2C_REG(CAT_LENS, CATA_AF_STATUS, 1) | ||
322 | #define REG_AF_FAIL 0x00 | ||
323 | #define REG_AF_SUCCESS 0x02 | ||
324 | #define REG_AF_IDLE 0x04 | ||
325 | #define REG_AF_BUSY 0x05 | ||
326 | |||
327 | #define AF_VERSION I2C_REG(CAT_LENS, CATA_AF_VERSION, 1) | ||
328 | |||
329 | /* | ||
330 | * Category B - CAPTURE Parameter | ||
331 | */ | ||
332 | #define CATB_YUVOUT_MAIN 0x00 | ||
333 | #define CATB_MAIN_IMAGE_SIZE 0x01 | ||
334 | #define CATB_MCC_MODE 0x1d | ||
335 | #define CATB_WDR_EN 0x2c | ||
336 | #define CATB_LIGHT_CTRL 0x40 | ||
337 | #define CATB_FLASH_CTRL 0x41 | ||
338 | |||
339 | #define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1) | ||
340 | #define REG_YUV422 0x00 | ||
341 | #define REG_BAYER10 0x05 | ||
342 | #define REG_BAYER8 0x06 | ||
343 | #define REG_JPEG 0x10 | ||
344 | |||
345 | #define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1) | ||
346 | |||
347 | #define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1) | ||
348 | #define REG_MCC_OFF 0x00 | ||
349 | #define REG_MCC_NORMAL 0x01 | ||
350 | |||
351 | #define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1) | ||
352 | #define REG_WDR_OFF 0x00 | ||
353 | #define REG_WDR_ON 0x01 | ||
354 | #define REG_WDR_AUTO 0x02 | ||
355 | |||
356 | #define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1) | ||
357 | #define REG_LIGHT_OFF 0x00 | ||
358 | #define REG_LIGHT_ON 0x01 | ||
359 | #define REG_LIGHT_AUTO 0x02 | ||
360 | |||
361 | #define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1) | ||
362 | #define REG_FLASH_OFF 0x00 | ||
363 | #define REG_FLASH_ON 0x01 | ||
364 | #define REG_FLASH_AUTO 0x02 | ||
365 | |||
366 | /* | ||
367 | * Category C - CAPTURE Control | ||
368 | */ | ||
369 | #define CATC_CAP_MODE 0x00 | ||
370 | #define CATC_CAP_SEL_FRAME 0x06 /* It determines Single or Multi */ | ||
371 | #define CATC_CAP_START 0x09 | ||
372 | #define CATC_CAP_IMAGE_SIZE 0x0d | ||
373 | #define CATC_CAP_THUMB_SIZE 0x11 | ||
374 | |||
375 | #define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1) | ||
376 | #define REG_CAP_NONE 0x00 | ||
377 | #define REG_CAP_ANTI_SHAKE 0x02 | ||
378 | |||
379 | #define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1) | ||
380 | |||
381 | #define CAPC_START I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1) | ||
382 | #define REG_CAP_START_MAIN 0x01 | ||
383 | #define REG_CAP_START_THUMB 0x03 | ||
384 | |||
385 | #define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1) | ||
386 | #define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1) | ||
387 | |||
388 | /* | ||
389 | * Category F - Flash | ||
390 | * | ||
391 | * This mode provides functions about internal flash stuff and system startup. | ||
392 | */ | ||
393 | #define CATF_CAM_START 0x12 /* It starts internal ARM core booting | ||
394 | * after power-up */ | ||
395 | |||
396 | #define FLASH_CAM_START I2C_REG(CAT_FLASH, CATF_CAM_START, 1) | ||
397 | #define REG_START_ARM_BOOT 0x01 | ||
398 | |||
399 | #endif /* M5MOLS_REG_H */ | ||
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile index 968c1994eda0..2071ca8a2f03 100644 --- a/drivers/media/video/uvc/Makefile +++ b/drivers/media/video/uvc/Makefile | |||
@@ -1,3 +1,6 @@ | |||
1 | uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ | 1 | uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ |
2 | uvc_status.o uvc_isight.o | 2 | uvc_status.o uvc_isight.o |
3 | ifeq ($(CONFIG_MEDIA_CONTROLLER),y) | ||
4 | uvcvideo-objs += uvc_entity.o | ||
5 | endif | ||
3 | obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o | 6 | obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o |
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 823f4b389745..b6eae48d7fb8 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c | |||
@@ -248,7 +248,7 @@ uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) | |||
248 | * Terminal and unit management | 248 | * Terminal and unit management |
249 | */ | 249 | */ |
250 | 250 | ||
251 | static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) | 251 | struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) |
252 | { | 252 | { |
253 | struct uvc_entity *entity; | 253 | struct uvc_entity *entity; |
254 | 254 | ||
@@ -795,9 +795,12 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, | |||
795 | struct uvc_entity *entity; | 795 | struct uvc_entity *entity; |
796 | unsigned int num_inputs; | 796 | unsigned int num_inputs; |
797 | unsigned int size; | 797 | unsigned int size; |
798 | unsigned int i; | ||
798 | 799 | ||
800 | extra_size = ALIGN(extra_size, sizeof(*entity->pads)); | ||
799 | num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; | 801 | num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; |
800 | size = sizeof(*entity) + extra_size + num_inputs; | 802 | size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads |
803 | + num_inputs; | ||
801 | entity = kzalloc(size, GFP_KERNEL); | 804 | entity = kzalloc(size, GFP_KERNEL); |
802 | if (entity == NULL) | 805 | if (entity == NULL) |
803 | return NULL; | 806 | return NULL; |
@@ -805,8 +808,17 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, | |||
805 | entity->id = id; | 808 | entity->id = id; |
806 | entity->type = type; | 809 | entity->type = type; |
807 | 810 | ||
811 | entity->num_links = 0; | ||
812 | entity->num_pads = num_pads; | ||
813 | entity->pads = ((void *)(entity + 1)) + extra_size; | ||
814 | |||
815 | for (i = 0; i < num_inputs; ++i) | ||
816 | entity->pads[i].flags = MEDIA_PAD_FL_SINK; | ||
817 | if (!UVC_ENTITY_IS_OTERM(entity)) | ||
818 | entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; | ||
819 | |||
808 | entity->bNrInPins = num_inputs; | 820 | entity->bNrInPins = num_inputs; |
809 | entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; | 821 | entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); |
810 | 822 | ||
811 | return entity; | 823 | return entity; |
812 | } | 824 | } |
@@ -1585,6 +1597,13 @@ static void uvc_delete(struct uvc_device *dev) | |||
1585 | uvc_status_cleanup(dev); | 1597 | uvc_status_cleanup(dev); |
1586 | uvc_ctrl_cleanup_device(dev); | 1598 | uvc_ctrl_cleanup_device(dev); |
1587 | 1599 | ||
1600 | if (dev->vdev.dev) | ||
1601 | v4l2_device_unregister(&dev->vdev); | ||
1602 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
1603 | if (media_devnode_is_registered(&dev->mdev.devnode)) | ||
1604 | media_device_unregister(&dev->mdev); | ||
1605 | #endif | ||
1606 | |||
1588 | list_for_each_safe(p, n, &dev->chains) { | 1607 | list_for_each_safe(p, n, &dev->chains) { |
1589 | struct uvc_video_chain *chain; | 1608 | struct uvc_video_chain *chain; |
1590 | chain = list_entry(p, struct uvc_video_chain, list); | 1609 | chain = list_entry(p, struct uvc_video_chain, list); |
@@ -1594,6 +1613,13 @@ static void uvc_delete(struct uvc_device *dev) | |||
1594 | list_for_each_safe(p, n, &dev->entities) { | 1613 | list_for_each_safe(p, n, &dev->entities) { |
1595 | struct uvc_entity *entity; | 1614 | struct uvc_entity *entity; |
1596 | entity = list_entry(p, struct uvc_entity, list); | 1615 | entity = list_entry(p, struct uvc_entity, list); |
1616 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
1617 | uvc_mc_cleanup_entity(entity); | ||
1618 | #endif | ||
1619 | if (entity->vdev) { | ||
1620 | video_device_release(entity->vdev); | ||
1621 | entity->vdev = NULL; | ||
1622 | } | ||
1597 | kfree(entity); | 1623 | kfree(entity); |
1598 | } | 1624 | } |
1599 | 1625 | ||
@@ -1616,8 +1642,6 @@ static void uvc_release(struct video_device *vdev) | |||
1616 | struct uvc_streaming *stream = video_get_drvdata(vdev); | 1642 | struct uvc_streaming *stream = video_get_drvdata(vdev); |
1617 | struct uvc_device *dev = stream->dev; | 1643 | struct uvc_device *dev = stream->dev; |
1618 | 1644 | ||
1619 | video_device_release(vdev); | ||
1620 | |||
1621 | /* Decrement the registered streams count and delete the device when it | 1645 | /* Decrement the registered streams count and delete the device when it |
1622 | * reaches zero. | 1646 | * reaches zero. |
1623 | */ | 1647 | */ |
@@ -1682,7 +1706,7 @@ static int uvc_register_video(struct uvc_device *dev, | |||
1682 | * unregistered before the reference is released, so we don't need to | 1706 | * unregistered before the reference is released, so we don't need to |
1683 | * get another one. | 1707 | * get another one. |
1684 | */ | 1708 | */ |
1685 | vdev->parent = &dev->intf->dev; | 1709 | vdev->v4l2_dev = &dev->vdev; |
1686 | vdev->fops = &uvc_fops; | 1710 | vdev->fops = &uvc_fops; |
1687 | vdev->release = uvc_release; | 1711 | vdev->release = uvc_release; |
1688 | strlcpy(vdev->name, dev->name, sizeof vdev->name); | 1712 | strlcpy(vdev->name, dev->name, sizeof vdev->name); |
@@ -1731,6 +1755,8 @@ static int uvc_register_terms(struct uvc_device *dev, | |||
1731 | ret = uvc_register_video(dev, stream); | 1755 | ret = uvc_register_video(dev, stream); |
1732 | if (ret < 0) | 1756 | if (ret < 0) |
1733 | return ret; | 1757 | return ret; |
1758 | |||
1759 | term->vdev = stream->vdev; | ||
1734 | } | 1760 | } |
1735 | 1761 | ||
1736 | return 0; | 1762 | return 0; |
@@ -1745,6 +1771,14 @@ static int uvc_register_chains(struct uvc_device *dev) | |||
1745 | ret = uvc_register_terms(dev, chain); | 1771 | ret = uvc_register_terms(dev, chain); |
1746 | if (ret < 0) | 1772 | if (ret < 0) |
1747 | return ret; | 1773 | return ret; |
1774 | |||
1775 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
1776 | ret = uvc_mc_register_entities(chain); | ||
1777 | if (ret < 0) { | ||
1778 | uvc_printk(KERN_INFO, "Failed to register entites " | ||
1779 | "(%d).\n", ret); | ||
1780 | } | ||
1781 | #endif | ||
1748 | } | 1782 | } |
1749 | 1783 | ||
1750 | return 0; | 1784 | return 0; |
@@ -1814,6 +1848,24 @@ static int uvc_probe(struct usb_interface *intf, | |||
1814 | "linux-uvc-devel mailing list.\n"); | 1848 | "linux-uvc-devel mailing list.\n"); |
1815 | } | 1849 | } |
1816 | 1850 | ||
1851 | /* Register the media and V4L2 devices. */ | ||
1852 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
1853 | dev->mdev.dev = &intf->dev; | ||
1854 | strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); | ||
1855 | if (udev->serial) | ||
1856 | strlcpy(dev->mdev.serial, udev->serial, | ||
1857 | sizeof(dev->mdev.serial)); | ||
1858 | strcpy(dev->mdev.bus_info, udev->devpath); | ||
1859 | dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); | ||
1860 | dev->mdev.driver_version = DRIVER_VERSION_NUMBER; | ||
1861 | if (media_device_register(&dev->mdev) < 0) | ||
1862 | goto error; | ||
1863 | |||
1864 | dev->vdev.mdev = &dev->mdev; | ||
1865 | #endif | ||
1866 | if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) | ||
1867 | goto error; | ||
1868 | |||
1817 | /* Initialize controls. */ | 1869 | /* Initialize controls. */ |
1818 | if (uvc_ctrl_init_device(dev) < 0) | 1870 | if (uvc_ctrl_init_device(dev) < 0) |
1819 | goto error; | 1871 | goto error; |
@@ -1822,7 +1874,7 @@ static int uvc_probe(struct usb_interface *intf, | |||
1822 | if (uvc_scan_device(dev) < 0) | 1874 | if (uvc_scan_device(dev) < 0) |
1823 | goto error; | 1875 | goto error; |
1824 | 1876 | ||
1825 | /* Register video devices. */ | 1877 | /* Register video device nodes. */ |
1826 | if (uvc_register_chains(dev) < 0) | 1878 | if (uvc_register_chains(dev) < 0) |
1827 | goto error; | 1879 | goto error; |
1828 | 1880 | ||
diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c new file mode 100644 index 000000000000..ede7852bb1df --- /dev/null +++ b/drivers/media/video/uvc/uvc_entity.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * uvc_entity.c -- USB Video Class driver | ||
3 | * | ||
4 | * Copyright (C) 2005-2011 | ||
5 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/list.h> | ||
16 | #include <linux/videodev2.h> | ||
17 | |||
18 | #include <media/v4l2-common.h> | ||
19 | |||
20 | #include "uvcvideo.h" | ||
21 | |||
22 | /* ------------------------------------------------------------------------ | ||
23 | * Video subdevices registration and unregistration | ||
24 | */ | ||
25 | |||
26 | static int uvc_mc_register_entity(struct uvc_video_chain *chain, | ||
27 | struct uvc_entity *entity) | ||
28 | { | ||
29 | const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; | ||
30 | struct uvc_entity *remote; | ||
31 | unsigned int i; | ||
32 | u8 remote_pad; | ||
33 | int ret; | ||
34 | |||
35 | for (i = 0; i < entity->num_pads; ++i) { | ||
36 | struct media_entity *source; | ||
37 | struct media_entity *sink; | ||
38 | |||
39 | if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) | ||
40 | continue; | ||
41 | |||
42 | remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); | ||
43 | if (remote == NULL) | ||
44 | return -EINVAL; | ||
45 | |||
46 | source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) | ||
47 | ? &remote->vdev->entity : &remote->subdev.entity; | ||
48 | sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) | ||
49 | ? &entity->vdev->entity : &entity->subdev.entity; | ||
50 | |||
51 | remote_pad = remote->num_pads - 1; | ||
52 | ret = media_entity_create_link(source, remote_pad, | ||
53 | sink, i, flags); | ||
54 | if (ret < 0) | ||
55 | return ret; | ||
56 | } | ||
57 | |||
58 | if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) | ||
59 | ret = v4l2_device_register_subdev(&chain->dev->vdev, | ||
60 | &entity->subdev); | ||
61 | |||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static struct v4l2_subdev_ops uvc_subdev_ops = { | ||
66 | }; | ||
67 | |||
68 | void uvc_mc_cleanup_entity(struct uvc_entity *entity) | ||
69 | { | ||
70 | if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) | ||
71 | media_entity_cleanup(&entity->subdev.entity); | ||
72 | else if (entity->vdev != NULL) | ||
73 | media_entity_cleanup(&entity->vdev->entity); | ||
74 | } | ||
75 | |||
76 | static int uvc_mc_init_entity(struct uvc_entity *entity) | ||
77 | { | ||
78 | int ret; | ||
79 | |||
80 | if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { | ||
81 | v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); | ||
82 | strlcpy(entity->subdev.name, entity->name, | ||
83 | sizeof(entity->subdev.name)); | ||
84 | |||
85 | ret = media_entity_init(&entity->subdev.entity, | ||
86 | entity->num_pads, entity->pads, 0); | ||
87 | } else | ||
88 | ret = media_entity_init(&entity->vdev->entity, | ||
89 | entity->num_pads, entity->pads, 0); | ||
90 | |||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | int uvc_mc_register_entities(struct uvc_video_chain *chain) | ||
95 | { | ||
96 | struct uvc_entity *entity; | ||
97 | int ret; | ||
98 | |||
99 | list_for_each_entry(entity, &chain->entities, chain) { | ||
100 | ret = uvc_mc_init_entity(entity); | ||
101 | if (ret < 0) { | ||
102 | uvc_printk(KERN_INFO, "Failed to initialize entity for " | ||
103 | "entity %u\n", entity->id); | ||
104 | return ret; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | list_for_each_entry(entity, &chain->entities, chain) { | ||
109 | ret = uvc_mc_register_entity(chain, entity); | ||
110 | if (ret < 0) { | ||
111 | uvc_printk(KERN_INFO, "Failed to register entity for " | ||
112 | "entity %u\n", entity->id); | ||
113 | return ret; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | return 0; | ||
118 | } | ||
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 7cf224bae2e5..20107fd3574d 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -98,8 +98,11 @@ struct uvc_xu_control { | |||
98 | #ifdef __KERNEL__ | 98 | #ifdef __KERNEL__ |
99 | 99 | ||
100 | #include <linux/poll.h> | 100 | #include <linux/poll.h> |
101 | #include <linux/usb.h> | ||
101 | #include <linux/usb/video.h> | 102 | #include <linux/usb/video.h> |
102 | #include <linux/uvcvideo.h> | 103 | #include <linux/uvcvideo.h> |
104 | #include <media/media-device.h> | ||
105 | #include <media/v4l2-device.h> | ||
103 | 106 | ||
104 | /* -------------------------------------------------------------------------- | 107 | /* -------------------------------------------------------------------------- |
105 | * UVC constants | 108 | * UVC constants |
@@ -301,6 +304,13 @@ struct uvc_entity { | |||
301 | __u16 type; | 304 | __u16 type; |
302 | char name[64]; | 305 | char name[64]; |
303 | 306 | ||
307 | /* Media controller-related fields. */ | ||
308 | struct video_device *vdev; | ||
309 | struct v4l2_subdev subdev; | ||
310 | unsigned int num_pads; | ||
311 | unsigned int num_links; | ||
312 | struct media_pad *pads; | ||
313 | |||
304 | union { | 314 | union { |
305 | struct { | 315 | struct { |
306 | __u16 wObjectiveFocalLengthMin; | 316 | __u16 wObjectiveFocalLengthMin; |
@@ -504,6 +514,10 @@ struct uvc_device { | |||
504 | atomic_t nmappings; | 514 | atomic_t nmappings; |
505 | 515 | ||
506 | /* Video control interface */ | 516 | /* Video control interface */ |
517 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
518 | struct media_device mdev; | ||
519 | #endif | ||
520 | struct v4l2_device vdev; | ||
507 | __u16 uvc_version; | 521 | __u16 uvc_version; |
508 | __u32 clock_frequency; | 522 | __u32 clock_frequency; |
509 | 523 | ||
@@ -583,6 +597,8 @@ extern unsigned int uvc_timeout_param; | |||
583 | /* Core driver */ | 597 | /* Core driver */ |
584 | extern struct uvc_driver uvc_driver; | 598 | extern struct uvc_driver uvc_driver; |
585 | 599 | ||
600 | extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); | ||
601 | |||
586 | /* Video buffers queue management. */ | 602 | /* Video buffers queue management. */ |
587 | extern void uvc_queue_init(struct uvc_video_queue *queue, | 603 | extern void uvc_queue_init(struct uvc_video_queue *queue, |
588 | enum v4l2_buf_type type, int drop_corrupted); | 604 | enum v4l2_buf_type type, int drop_corrupted); |
@@ -616,6 +632,10 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) | |||
616 | /* V4L2 interface */ | 632 | /* V4L2 interface */ |
617 | extern const struct v4l2_file_operations uvc_fops; | 633 | extern const struct v4l2_file_operations uvc_fops; |
618 | 634 | ||
635 | /* Media controller */ | ||
636 | extern int uvc_mc_register_entities(struct uvc_video_chain *chain); | ||
637 | extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); | ||
638 | |||
619 | /* Video */ | 639 | /* Video */ |
620 | extern int uvc_video_init(struct uvc_streaming *stream); | 640 | extern int uvc_video_init(struct uvc_streaming *stream); |
621 | extern int uvc_video_suspend(struct uvc_streaming *stream); | 641 | extern int uvc_video_suspend(struct uvc_streaming *stream); |